home *** CD-ROM | disk | FTP | other *** search
/ Personal Computer World 2009 June / PersonalComputerWorld-June2009-CoverdiscCD.iso / Software / Freeware / Firebug 1.3.3 / firebug-1.3.3-fx.xpi / content / firebug / debugger.js < prev    next >
Encoding:
JavaScript  |  2009-02-19  |  86.0 KB  |  2,857 lines

  1. /* See license.txt for terms of usage */
  2.  
  3. FBL.ns(function() { with (FBL) {
  4.  
  5. // ************************************************************************************************
  6. // Constants
  7.  
  8. const Cc = Components.classes;
  9. const Ci = Components.interfaces;
  10. const jsdIScript = Ci.jsdIScript;
  11. const jsdIStackFrame = Ci.jsdIStackFrame;
  12. const jsdIExecutionHook = Ci.jsdIExecutionHook;
  13. const nsISupports = Ci.nsISupports;
  14. const nsICryptoHash = Ci.nsICryptoHash;
  15. const nsIURI = Ci.nsIURI;
  16.  
  17. const PCMAP_SOURCETEXT = jsdIScript.PCMAP_SOURCETEXT;
  18.  
  19. const RETURN_VALUE = jsdIExecutionHook.RETURN_RET_WITH_VAL;
  20. const RETURN_THROW_WITH_VAL = jsdIExecutionHook.RETURN_THROW_WITH_VAL;
  21. const RETURN_CONTINUE = jsdIExecutionHook.RETURN_CONTINUE;
  22. const RETURN_CONTINUE_THROW = jsdIExecutionHook.RETURN_CONTINUE_THROW;
  23. const RETURN_ABORT = jsdIExecutionHook.RETURN_ABORT;
  24.  
  25. const TYPE_THROW = jsdIExecutionHook.TYPE_THROW;
  26.  
  27. const STEP_OVER = 1;
  28. const STEP_INTO = 2;
  29. const STEP_OUT = 3;
  30.  
  31. // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  32.  
  33. const tooltipTimeout = 300;
  34.  
  35. const reLineNumber = /^[^\\]?#(\d*)$/;
  36.  
  37. const reEval =  /\s*eval\s*\(([^)]*)\)/m;        // eval ( $1 )
  38. const reHTM = /\.[hH][tT][mM]/;
  39. const reFunction = /\s*Function\s*\(([^)]*)\)/m;
  40.  
  41. const panelStatus = $("fbPanelStatus");
  42.  
  43. // ************************************************************************************************
  44.  
  45. var listeners = [];
  46.  
  47. // ************************************************************************************************
  48.  
  49. Firebug.Debugger = extend(Firebug.ActivableModule,
  50. {
  51.     fbs: fbs, // access to firebug-service in chromebug under browser.xul.DOM.Firebug.Debugger.fbs /*@explore*/
  52.     // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  53.     // Debugging
  54.  
  55.     evaluate: function(js, context, scope)
  56.     {
  57.         var frame = context.currentFrame;
  58.         if (!frame)
  59.             return;
  60.  
  61.         frame.scope.refresh(); // XXX what's this do?
  62.  
  63.         var result = {};
  64.         var scriptToEval = js;
  65.         if (scope && scope.thisValue) {
  66.             // XXX need to stick scope.thisValue somewhere... frame.scope.globalObject?
  67.             scriptToEval = " (function() { return " + js + " }).apply(__thisValue__);";
  68.         }
  69.  
  70.         // This seem to be safe; eval'ing a getter property in content that tries to
  71.         // be evil and get Components.classes results in a permission denied error.
  72.         var ok = frame.eval(scriptToEval, "", 1, result);
  73.  
  74.         var value = result.value.getWrappedValue();
  75.         if (ok)
  76.             return value;
  77.         else
  78.             throw value;
  79.     },
  80.  
  81.     getCurrentFrameKeys: function(context)
  82.     {
  83.         var globals = keys(context.window.wrappedJSObject);  // return is safe
  84.  
  85.         if (context.currentFrame)
  86.             return this.getFrameKeys(context.currentFrame, globals);
  87.  
  88.         return globals;
  89.     },
  90.  
  91.     getFrameKeys: function(frame, names)
  92.     {
  93.         var listValue = {value: null}, lengthValue = {value: 0};
  94.         frame.scope.getProperties(listValue, lengthValue);
  95.  
  96.         for (var i = 0; i < lengthValue.value; ++i)
  97.         {
  98.             var prop = listValue.value[i];
  99.             var name = prop.name.getWrappedValue();
  100.             names.push(name);
  101.         }
  102.         return names;
  103.     },
  104.  
  105.     focusWatch: function(context)
  106.     {
  107.         if (context.detached)
  108.             context.chrome.focus();
  109.         else
  110.             Firebug.toggleBar(true);
  111.  
  112.         context.chrome.selectPanel("script");
  113.  
  114.         var watchPanel = context.getPanel("watches", true);
  115.         if (watchPanel)
  116.         {
  117.             Firebug.CommandLine.isNeededGetReady(context);
  118.             watchPanel.editNewWatch();
  119.         }
  120.     },
  121.  
  122.     // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  123.     beginInternalOperation: function() // stop debugger operations like breakOnErrors
  124.     {
  125.         var state = {breakOnErrors: Firebug.breakOnErrors};
  126.         Firebug.breakOnErrors = false;
  127.         return state;
  128.     },
  129.  
  130.     endInternalOperation: function(state)  // pass back the object given by beginInternalOperation
  131.     {
  132.         Firebug.breakOnErrors = state.breakOnErrors;
  133.         return true;
  134.     },
  135.     // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  136.  
  137.     halt: function(fn)
  138.     {
  139.         this.haltCallback = fn;
  140.         fbs.halt(this);
  141.  
  142.         debugger; // For some reason this is not reliable.
  143.         if (this.haltCallback) // so we have a second try
  144.         {
  145.             if (Firebug.CommandLine.isNeededGetReady(FirebugContext))
  146.                 Firebug.CommandLine.evaluate("debugger;", FirebugContext);
  147.         }
  148.  
  149.     },
  150.  
  151.     stop: function(context, frame, type, rv)
  152.     {
  153.         if (context.stopped)
  154.             return RETURN_CONTINUE;
  155.  
  156.         if (!this.isEnabled(context))
  157.             return RETURN_CONTINUE;
  158.  
  159.         var executionContext;
  160.         try
  161.         {
  162.             executionContext = frame.executionContext;
  163.         }
  164.         catch (exc)
  165.         {
  166.             // Can't proceed with an execution context - it happens sometimes.
  167.             return RETURN_CONTINUE;
  168.         }
  169.  
  170.         context.debugFrame = frame;
  171.         context.stopped = true;
  172.  
  173.         var hookReturn = dispatch2(listeners,"onStop",[context,frame, type,rv]);
  174.         if ( hookReturn && hookReturn >= 0 )
  175.         {
  176.             delete context.stopped;
  177.             delete context.debugFrame;
  178.             delete context;
  179.             return hookReturn;
  180.         }
  181.  
  182.         try {
  183.             executionContext.scriptsEnabled = false;
  184.  
  185.             cacheAllScripts(context);
  186.  
  187.         } catch (exc) {
  188.             // This attribute is only valid for contexts which implement nsIScriptContext.
  189.         }
  190.  
  191.         try
  192.         {
  193.             // We will pause here until resume is called
  194.             var depth = fbs.enterNestedEventLoop({onNest: bindFixed(this.startDebugging, this, context)});
  195.             // For some reason we don't always end up here
  196.         }
  197.         catch (exc)
  198.         {
  199.             // Just ignore exceptions that happened while in the nested loop
  200.         }
  201.  
  202.         try {
  203.             if (executionContext.isValid)
  204.             {
  205.                 executionContext.scriptsEnabled = true;
  206.             }
  207.             else
  208.             {
  209.             }
  210.         } catch (exc) {
  211.         }
  212.  
  213.         this.stopDebugging(context);
  214.  
  215.         dispatch(listeners,"onResume",[context]);
  216.  
  217.         if (this.aborted)
  218.         {
  219.             delete this.aborted;
  220.             return RETURN_ABORT;
  221.         }
  222.         else
  223.             return RETURN_CONTINUE;
  224.     },
  225.  
  226.     resume: function(context)
  227.     {
  228.         if (!context.stopped)
  229.         {
  230.             this.syncCommands(context);
  231.             return;
  232.         }
  233.  
  234.         delete context.stopped;
  235.         delete context.debugFrame;
  236.         delete context.currentFrame;
  237.         delete context;
  238.  
  239.         var depth = fbs.exitNestedEventLoop();
  240.     },
  241.  
  242.     abort: function(context)
  243.     {
  244.         if (context.stopped)
  245.         {
  246.             context.aborted = true;
  247.             this.resume(context);
  248.         }
  249.     },
  250.  
  251.     stepOver: function(context)
  252.     {
  253.         if (!context.debugFrame || !context.debugFrame.isValid)
  254.             return;
  255.  
  256.         fbs.step(STEP_OVER, context.debugFrame);
  257.         this.resume(context);
  258.     },
  259.  
  260.     stepInto: function(context)
  261.     {
  262.         if (!context.debugFrame.isValid)
  263.             return;
  264.  
  265.         fbs.step(STEP_INTO, context.debugFrame);
  266.         this.resume(context);
  267.     },
  268.  
  269.     stepOut: function(context)
  270.     {
  271.         if (!context.debugFrame.isValid)
  272.             return;
  273.  
  274.         fbs.step(STEP_OUT, context.debugFrame);
  275.         this.resume(context);
  276.     },
  277.  
  278.     suspend: function(context)
  279.     {
  280.         if (context.stopped)
  281.             return;
  282.         fbs.suspend();
  283.     },
  284.  
  285.     runUntil: function(context, sourceFile, lineNo)
  286.     {
  287.         if (!context.debugFrame.isValid)
  288.             return;
  289.  
  290.         fbs.runUntil(sourceFile, lineNo, context.debugFrame, this);
  291.         this.resume(context);
  292.     },
  293.  
  294.     // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  295.     // Breakpoints
  296.  
  297.     setBreakpoint: function(sourceFile, lineNo)
  298.     {
  299.         fbs.setBreakpoint(sourceFile, lineNo, null, Firebug.Debugger);
  300.     },
  301.  
  302.     clearBreakpoint: function(sourceFile, lineNo)
  303.     {
  304.         fbs.clearBreakpoint(sourceFile.href, lineNo);
  305.     },
  306.  
  307.     setErrorBreakpoint: function(sourceFile, line)
  308.     {
  309.         fbs.setErrorBreakpoint(sourceFile, line, Firebug.Debugger);
  310.     },
  311.  
  312.     clearErrorBreakpoint: function(sourceFile, line)
  313.     {
  314.         fbs.clearErrorBreakpoint(sourceFile.href, line, Firebug.Debugger);
  315.     },
  316.  
  317.     enableErrorBreakpoint: function(sourceFile, line)
  318.     {
  319.         fbs.enableErrorBreakpoint(sourceFile, line, Firebug.Debugger);
  320.     },
  321.  
  322.     disableErrorBreakpoint: function(sourceFile, line)
  323.     {
  324.         fbs.disableErrorBreakpoint(sourceFile, line, Firebug.Debugger);
  325.     },
  326.  
  327.     clearAllBreakpoints: function(context)
  328.     {
  329.         var sourceFiles = sourceFilesAsArray(context);
  330.         fbs.clearAllBreakpoints(sourceFiles, Firebug.Debugger);
  331.     },
  332.  
  333.     enableAllBreakpoints: function(context)
  334.     {
  335.         for (var url in context.sourceFileMap)
  336.         {
  337.             fbs.enumerateBreakpoints(url, {call: function(url, lineNo)
  338.             {
  339.                 fbs.enableBreakpoint(url, lineNo);
  340.             }});
  341.         }
  342.     },
  343.  
  344.     disableAllBreakpoints: function(context)
  345.     {
  346.         for (var url in context.sourceFileMap)
  347.         {
  348.             fbs.enumerateBreakpoints(url, {call: function(url, lineNo)
  349.             {
  350.                 fbs.disableBreakpoint(url, lineNo);
  351.             }});
  352.         }
  353.     },
  354.  
  355.     getBreakpointCount: function(context)
  356.     {
  357.         var count = 0;
  358.         for (var url in context.sourceFileMap)
  359.         {
  360.             fbs.enumerateBreakpoints(url,
  361.             {
  362.                 call: function(url, lineNo)
  363.                 {
  364.                     ++count;
  365.                 }
  366.             });
  367.  
  368.             fbs.enumerateErrorBreakpoints(url,
  369.             {
  370.                 call: function(url, lineNo)
  371.                 {
  372.                     ++count;
  373.                 }
  374.             });
  375.         }
  376.         return count;
  377.     },
  378.  
  379.     // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  380.     // Debugging and monitoring
  381.  
  382.     trace: function(fn, object, mode)
  383.     {
  384.         if (typeof(fn) == "function" || fn instanceof Function)
  385.         {
  386.             var script = findScriptForFunctionInContext(FirebugContext, fn);
  387.             if (script)
  388.                 this.traceFunction(fn, script, mode);
  389.         }
  390.     },
  391.  
  392.     untrace: function(fn, object, mode)
  393.     {
  394.         if (typeof(fn) == "function" || fn instanceof Function)
  395.         {
  396.             var script = findScriptForFunctionInContext(FirebugContext, fn);
  397.             if (script)
  398.                 this.untraceFunction(fn, script, mode);
  399.         }
  400.     },
  401.  
  402.     traceFunction: function(fn, script, mode)
  403.     {
  404.         var scriptInfo = getSourceFileAndLineByScript(FirebugContext, script);
  405.         if (scriptInfo)
  406.         {
  407.             if (mode == "debug")
  408.                 this.setBreakpoint(scriptInfo.sourceFile, scriptInfo.lineNo, null, this);
  409.                else if (mode == "monitor")
  410.                 fbs.monitor(scriptInfo.sourceFile, scriptInfo.lineNo, Firebug.Debugger);
  411.         }
  412.     },
  413.  
  414.     untraceFunction: function(fn, script, mode)
  415.     {
  416.         var scriptInfo = getSourceFileAndLineByScript(FirebugContext, script);
  417.         if (scriptInfo)
  418.         {
  419.             if (mode == "debug")
  420.                 this.clearBreakpoint(scriptInfo.sourceFile, scriptInfo.lineNo);
  421.             else if (mode == "monitor")
  422.                 fbs.unmonitor(scriptInfo.sourceFile, scriptInfo.lineNo);
  423.         }
  424.     },
  425.  
  426.     // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  427.     // UI Stuff
  428.  
  429.     startDebugging: function(context)
  430.     {
  431.         try {
  432.             fbs.lockDebugger();
  433.  
  434.             context.currentFrame = context.debugFrame;
  435.  
  436.             // Make FirebugContext = context and sync the UI
  437.             var browser = context.browser;
  438.             browser.chrome.showContext(browser, context);
  439.  
  440.             this.syncCommands(context);
  441.             this.syncListeners(context);
  442.  
  443.             const updateViewOnShowHook = function()
  444.             {
  445.                 Firebug.toggleBar(true);
  446.  
  447.                 FirebugChrome.select(context.currentFrame, "script");
  448.  
  449.                 var stackPanel = context.getPanel("callstack");
  450.                 if (stackPanel)
  451.                     stackPanel.refresh(context);
  452.  
  453.                 context.chrome.focus();
  454.             }
  455.             if ( !context.hideDebuggerUI || (FirebugChrome.getCurrentBrowser() && FirebugChrome.getCurrentBrowser().showFirebug))
  456.                  updateViewOnShowHook();
  457.             else {
  458.                  context.chrome.updateViewOnShowHook = updateViewOnShowHook;
  459.             }
  460.  
  461.         }
  462.         catch(exc)
  463.         {
  464.         }
  465.     },
  466.  
  467.     stopDebugging: function(context)
  468.     {
  469.         try
  470.         {
  471.             fbs.unlockDebugger();
  472.  
  473.             // If the user reloads the page while the debugger is stopped, then
  474.             // the current context will be destroyed just before
  475.             if (context && context.window)
  476.             {
  477.                 var chrome = context.chrome;
  478.                 if (!chrome)
  479.                     chrome = FirebugChrome;
  480.  
  481.                 if ( chrome.updateViewOnShowHook )
  482.                     delete chrome.updateViewOnShowHook;
  483.  
  484.                 this.syncCommands(context);
  485.                 this.syncListeners(context);
  486.  
  487.                 if (FirebugContext && !FirebugContext.panelName) // XXXjjb all I know is that syncSidePanels() needs this set
  488.                     FirebugContext.panelName = "script";
  489.  
  490.                 chrome.syncSidePanels();
  491.  
  492.                 var panel = context.getPanel("script", true);
  493.                 if (panel)
  494.                 {
  495.                     panel.showNoStackFrame();
  496.                     panel.select(null);
  497.                 }
  498.             }
  499.         }
  500.         catch (exc)
  501.         {
  502.             ERROR(exc);
  503.         }
  504.     },
  505.  
  506.     syncCommands: function(context)
  507.     {
  508.         var chrome = context.chrome;
  509.         if (!chrome)
  510.         {
  511.             return;
  512.         }
  513.  
  514.         if (context.stopped)
  515.         {
  516.             chrome.setGlobalAttribute("fbDebuggerButtons", "stopped", "true");
  517.             chrome.setGlobalAttribute("cmd_resumeExecution", "disabled", "false");
  518.             chrome.setGlobalAttribute("cmd_stepOver", "disabled", "false");
  519.             chrome.setGlobalAttribute("cmd_stepInto", "disabled", "false");
  520.             chrome.setGlobalAttribute("cmd_stepOut", "disabled", "false");
  521.         }
  522.         else
  523.         {
  524.             chrome.setGlobalAttribute("fbDebuggerButtons", "stopped", "true");
  525.             chrome.setGlobalAttribute("cmd_resumeExecution", "disabled", "true");
  526.             chrome.setGlobalAttribute("cmd_stepOver", "disabled", "true");
  527.             chrome.setGlobalAttribute("cmd_stepInto", "disabled", "true");
  528.             chrome.setGlobalAttribute("cmd_stepOut", "disabled", "true");
  529.         }
  530.     },
  531.  
  532.     syncListeners: function(context)
  533.     {
  534.         var chrome = context.chrome;
  535.         if (!chrome)
  536.             chrome = FirebugChrome;
  537.  
  538.         if (context.stopped)
  539.             this.attachListeners(context, chrome);
  540.         else
  541.             this.detachListeners(context, chrome);
  542.     },
  543.  
  544.     attachListeners: function(context, chrome)
  545.     {
  546.         this.keyListeners =
  547.         [
  548.             chrome.keyCodeListen("F8", null, bind(this.resume, this, context), true),
  549.             chrome.keyListen("/", isControl, bind(this.resume, this, context)),
  550.             chrome.keyCodeListen("F10", null, bind(this.stepOver, this, context), true),
  551.             chrome.keyListen("'", isControl, bind(this.stepOver, this, context)),
  552.             chrome.keyCodeListen("F11", null, bind(this.stepInto, this, context)),
  553.             chrome.keyListen(";", isControl, bind(this.stepInto, this, context)),
  554.             chrome.keyCodeListen("F11", isShift, bind(this.stepOut, this, context)),
  555.             chrome.keyListen(",", isControlShift, bind(this.stepOut, this, context))
  556.         ];
  557.     },
  558.  
  559.     detachListeners: function(context, chrome)
  560.     {
  561.         if (this.keyListeners)
  562.         {
  563.             for (var i = 0; i < this.keyListeners.length; ++i)
  564.                 chrome.keyIgnore(this.keyListeners[i]);
  565.             delete this.keyListeners;
  566.         }
  567.     },
  568.  
  569.     showPanel: function(browser, panel)
  570.     {
  571.         var chrome =  browser.chrome;
  572.         if (chrome.updateViewOnShowHook)
  573.         {
  574.             var updateViewOnShowHook = chrome.updateViewOnShowHook;
  575.             delete chrome.updateViewOnShowHook;
  576.             updateViewOnShowHook();
  577.         }
  578.  
  579.         if (panel)
  580.         {
  581.             this.syncCommands(panel.context);
  582.             this.ableWatchSidePanel(panel.context);
  583.         }
  584.     },
  585.  
  586.     // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  587.     // These are XUL window level call backs and should be moved into Firebug where is says nsIFirebugClient
  588.  
  589.     onJSDActivate: function(jsd)  // just before hooks are set
  590.     {
  591.         // this is just to get the timing right.
  592.         // we called by fbs as a "debuggr", (one per window) and we are re-dispatching to our listeners,
  593.         // Firebug.DebugListeners.
  594.         var active = this.setIsJSDActive();
  595.  
  596.         dispatch2(listeners,"onJSDActivate",[fbs]);
  597.     },
  598.  
  599.     onJSDDeactivate: function(jsd)
  600.     {
  601.         this.setIsJSDActive();
  602.         dispatch2(listeners,"onJSDDeactivate",[fbs]);
  603.     },
  604.  
  605.     setIsJSDActive: function()
  606.     {
  607.         var active = fbs.isJSDActive();
  608.         if (active)
  609.             $('fbStatusIcon').setAttribute("jsd", "on");
  610.         else
  611.             $('fbStatusIcon').setAttribute("jsd", "off");
  612.  
  613.         return active;
  614.     },
  615.  
  616.     suspendFirebug: function()
  617.     {
  618.         Firebug.suspendFirebug();
  619.     },
  620.  
  621.     resumeFirebug: function()
  622.     {
  623.         Firebug.resumeFirebug();
  624.     },
  625.     // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  626.  
  627.     supportsWindow: function(win)
  628.     {
  629.         var context = ( (win && TabWatcher) ? TabWatcher.getContextByWindow(win) : null);
  630.  
  631.         if (!this.isEnabled(context))
  632.             return false;
  633.  
  634.         this.breakContext = context;
  635.         return !!context;
  636.     },
  637.  
  638.     supportsGlobal: function(global) // This is call from fbs for almost all fbs operations
  639.     {
  640.         var context = (TabWatcher ? TabWatcher.getContextByWindow(global) : null);
  641.  
  642.         if (context)
  643.         {
  644.             // Apparently the global is a XPCSafeJSObjectWrapper that looks like a Window.
  645.             // Since this is method called a lot make a hacky fast check on _getFirebugConsoleElement
  646.             if (!global._getFirebugConsoleElement)
  647.             {
  648.                 if (Firebug.Console.isEnabled(context))
  649.                 {
  650.                     var consoleReady = Firebug.Console.isNeededGetReady(context, global);
  651.                 }
  652.                 else
  653.                 {
  654.                 }
  655.             }
  656.  
  657.             if (!this.isEnabled(context))
  658.                 return false;
  659.         }
  660.  
  661.         this.breakContext = context;
  662.         return !!context;
  663.     },
  664.  
  665.     onLock: function(state)
  666.     {
  667.         // XXXjoe For now, trying to see if it's ok to have multiple contexts
  668.         // debugging simultaneously - otherwise we need this
  669.         //if (this.context != this.debugContext)
  670.         {
  671.             // XXXjoe Disable step/continue buttons
  672.         }
  673.     },
  674.  
  675.     onBreak: function(frame, type)
  676.     {
  677.         try {
  678.             var context = this.breakContext;
  679.             delete this.breakContext;
  680.  
  681.             if (!context)
  682.                 context = getFrameContext(frame);
  683.             if (!context)
  684.                 return RETURN_CONTINUE;
  685.  
  686.             return this.stop(context, frame, type);
  687.         }
  688.         catch (exc)
  689.         {
  690.             throw exc;
  691.         }
  692.     },
  693.  
  694.     onHalt: function(frame)
  695.     {
  696.         var callback = this.haltCallback;
  697.         delete this.haltCallback;
  698.  
  699.         if (callback)
  700.             callback(frame);
  701.  
  702.         return RETURN_CONTINUE;
  703.     },
  704.  
  705.     onThrow: function(frame, rv)
  706.     {
  707.         // onThrow is called for throw and for any catch that does not succeed.
  708.         var context = this.breakContext;
  709.         delete this.breakContext;
  710.  
  711.         if (!context)
  712.         {
  713.             FBTrace.sysout("debugger.onThrow, no context, try to get from frame\n");
  714.             context = getFrameContext(frame);
  715.         }
  716.         if (!context)
  717.             return RETURN_CONTINUE_THROW;
  718.  
  719.         if (!fbs.trackThrowCatch)
  720.             return RETURN_CONTINUE_THROW;
  721.  
  722.         try
  723.         {
  724.             var isCatch = this.isCatchFromPreviousThrow(frame, context);
  725.             if (!isCatch)
  726.             {
  727.                 context.thrownStackTrace = getStackTrace(frame, context);
  728.             }
  729.             else
  730.             {
  731.             }
  732.         }
  733.         catch  (exc)
  734.         {
  735.             FBTrace.sysout("onThrow FAILS: "+exc+"\n");
  736.         }
  737.  
  738.         if (dispatch2(listeners,"onThrow",[context, frame, rv]))
  739.             return this.stop(context, frame, TYPE_THROW, rv);
  740.         return RETURN_CONTINUE_THROW;
  741.     },
  742.  
  743.     isCatchFromPreviousThrow: function(frame, context)
  744.     {
  745.         if (context.thrownStackTrace)
  746.         {
  747.             var trace = context.thrownStackTrace.frames;
  748.             if (trace.length > 1)  // top of stack is [0]
  749.             {
  750.                 var curFrame = frame;
  751.                 var curFrameSig = curFrame.script.tag +"."+curFrame.pc;
  752.                 for (var i = 1; i < trace.length; i++)
  753.                 {
  754.                     var preFrameSig = trace[i].signature();
  755.                     if (curFrameSig == preFrameSig)
  756.                     {
  757.                         return true;  // catch from previous throw (or do we need to compare whole stack?
  758.                     }
  759.                 }
  760.                 // We looked at the previous stack and did not match the current frame
  761.             }
  762.         }
  763.        return false;
  764.     },
  765.  
  766.     onCall: function(frame)
  767.     {
  768.         var context = this.breakContext;
  769.         delete this.breakContext;
  770.  
  771.         if (!context)
  772.             context = getFrameContext(frame);
  773.         if (!context)
  774.             return RETURN_CONTINUE;
  775.  
  776.         frame = getStackFrame(frame, context);
  777.         Firebug.Console.log(frame, context);
  778.     },
  779.  
  780.     onError: function(frame, error)
  781.     {
  782.         var context = this.breakContext;
  783.         delete this.breakContext;
  784.  
  785.         try
  786.         {
  787.             Firebug.errorStackTrace = getStackTrace(frame, context);
  788.             if (Firebug.breakOnErrors)
  789.                 Firebug.Errors.showMessageOnStatusBar(error);
  790.         }
  791.         catch (exc) {
  792.         }
  793.  
  794.         var hookReturn = dispatch2(listeners,"onError",[context, frame, error]);
  795.  
  796.         if (Firebug.breakOnErrors)
  797.             return -1;  // break
  798.  
  799.         if (hookReturn)
  800.             return hookReturn;
  801.  
  802.         return -2; /* let firebug service decide to break or not */
  803.     },
  804.  
  805.     onEvalScriptCreated: function(frame, outerScript, innerScripts)
  806.     {
  807.         try
  808.         {
  809.             var context = this.breakContext;
  810.             delete this.breakContext;
  811.  
  812.             var sourceFile = this.getEvalLevelSourceFile(frame, context, innerScripts);
  813.  
  814.             dispatch(listeners,"onEvalScriptCreated",[context, frame, sourceFile.href]);
  815.             return sourceFile;
  816.         }
  817.         catch (e)
  818.         {
  819.         }
  820.     },
  821.  
  822.     onEventScriptCreated: function(frame, outerScript, innerScripts)
  823.     {
  824.         var context = this.breakContext;
  825.         delete this.breakContext;
  826.  
  827.         var script = frame.script;
  828.         var creatorURL = normalizeURL(frame.script.fileName);
  829.  
  830.         try {
  831.             var source = script.functionSource;
  832.         } catch (exc) {
  833.             /*Bug 426692 */
  834.             var source = creatorURL + "/"+getUniqueId();
  835.         }
  836.  
  837.         var url = this.getDynamicURL(frame, source, "event");
  838.  
  839.         var lines = context.sourceCache.store(url, source);
  840.         var sourceFile = new FBL.EventSourceFile(url, frame.script, "event:"+script.functionName+"."+script.tag, lines, innerScripts);
  841.         context.sourceFileMap[url] = sourceFile;
  842.  
  843.         dispatch(listeners,"onEventScriptCreated",[context, frame, url]);
  844.         return sourceFile;
  845.     },
  846.  
  847.     // We just compiled a bunch of JS, eg a script tag in HTML.  We are about to run the outerScript.
  848.     onTopLevelScriptCreated: function(frame, outerScript, innerScripts)
  849.     {
  850.         var context = this.breakContext;
  851.         delete this.breakContext;
  852.  
  853.         // This is our only chance to get the linetable for the outerScript since it will run and be GC next.
  854.         var script = frame.script;
  855.         var url = normalizeURL(script.fileName);
  856.  
  857.         var sourceFile = context.sourceFileMap[url];
  858.         if (sourceFile && (sourceFile instanceof FBL.TopLevelSourceFile) )      // TODO test multiple script tags in one html file
  859.         {
  860.             if (!sourceFile.outerScript || !sourceFile.outerScript.isValid)
  861.                 sourceFile.outerScript = outerScript;
  862.             FBL.addScriptsToSourceFile(sourceFile, outerScript, innerScripts);
  863.         }
  864.         else
  865.         {
  866.             sourceFile = new FBL.TopLevelSourceFile(url, script, script.lineExtent, innerScripts);
  867.             context.sourceFileMap[url] = sourceFile;
  868.         }
  869.  
  870.         dispatch(listeners,"onTopLevelScriptCreated",[context, frame, sourceFile.href]);
  871.         return sourceFile;
  872.     },
  873.  
  874.  
  875.     onToggleBreakpoint: function(url, lineNo, isSet, props)
  876.     {
  877.         if (props.debugger != this) // then not for us
  878.         {
  879.             return;
  880.         }
  881.  
  882.         for (var i = 0; i < TabWatcher.contexts.length; ++i)
  883.         {
  884.             var panel = TabWatcher.contexts[i].getPanel("script", true);
  885.             if (panel)
  886.             {
  887.                 panel.context.invalidatePanels("breakpoints");
  888.  
  889.                 var sourceBox = panel.getSourceBoxByURL(url);
  890.                 if (sourceBox)
  891.                 {
  892.                     var row = sourceBox.getLineNode(lineNo);
  893.                     if (!row)
  894.                         continue;  // we *should* only be called for lines in the viewport...
  895.  
  896.                     row.setAttribute("breakpoint", isSet);
  897.                     if (isSet && props)
  898.                     {
  899.                         row.setAttribute("condition", props.condition ? "true" : "false");
  900.                         row.setAttribute("disabledBreakpoint", new Boolean(props.disabled).toString());
  901.                     } else
  902.                     {
  903.                         row.removeAttribute("condition");
  904.                         row.removeAttribute("disabledBreakpoint");
  905.                     }
  906.                 }
  907.                 else
  908.                 {
  909.                 }
  910.             }
  911.         }
  912.     },
  913.  
  914.     onToggleErrorBreakpoint: function(url, lineNo, isSet)
  915.     {
  916.         for (var i = 0; i < TabWatcher.contexts.length; ++i)
  917.         {
  918.             var panel = TabWatcher.contexts[i].getPanel("console", true);
  919.             if (panel)
  920.             {
  921.                 panel.context.invalidatePanels("breakpoints");
  922.  
  923.                 for (var row = panel.panelNode.firstChild; row; row = row.nextSibling)
  924.                 {
  925.                     var error = row.firstChild.repObject;
  926.                     if (error instanceof ErrorMessage && error.href == url && error.lineNo == lineNo)
  927.                     {
  928.                         if (isSet)
  929.                             setClass(row.firstChild, "breakForError");
  930.                         else
  931.                             removeClass(row.firstChild, "breakForError");
  932.                     }
  933.                 }
  934.             }
  935.         }
  936.     },
  937.  
  938.     onToggleMonitor: function(url, lineNo, isSet)
  939.     {
  940.         for (var i = 0; i < TabWatcher.contexts.length; ++i)
  941.         {
  942.             var panel = TabWatcher.contexts[i].getPanel("console", true);
  943.             if (panel)
  944.                 panel.context.invalidatePanels("breakpoints");
  945.         }
  946.     },
  947.  
  948.     // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  949.     // XXXjjb this code is not called, because I found the scheme for detecting Function too complex.
  950.     // I'm leaving it here to remind us that we need to support new Function().
  951.     onFunctionConstructor: function(frame, ctor_script)
  952.     {
  953.        try
  954.         {
  955.             var context = this.breakContext;
  956.             delete this.breakContext;
  957.  
  958.             var sourceFile = this.createSourceFileForFunctionConstructor(frame, ctor_script, context);
  959.  
  960.             dispatch(listeners,"onFunctionConstructor",[context, frame, ctor_script, sourceFile.href]);
  961.             return sourceFile.href;
  962.         }
  963.         catch(exc)
  964.         {
  965.             ERROR("debugger.onFunctionConstructor failed: "+exc);
  966.             return null;
  967.         }
  968.  
  969.     },
  970.  
  971.     createSourceFileForFunctionConstructor: function(caller_frame, ctor_script, context)
  972.     {
  973.         var ctor_expr = null; // this.getConstructorExpression(caller_frame, context);
  974.         if (ctor_expr)
  975.             var source  = this.getEvalBody(caller_frame, "lib.createSourceFileForFunctionConstructor ctor_expr", 1, ctor_expr);
  976.         else
  977.             var source = " bah createSourceFileForFunctionConstructor"; //ctor_script.functionSource;
  978.  
  979.         var url = this.getDynamicURL(frame, source, "Function");
  980.  
  981.         var lines =    context.sourceCache.store(url, source);
  982.         var sourceFile = new FBL.FunctionConstructorSourceFile(url, caller_frame.script, ctor_expr, lines.length);
  983.         context.sourceFileMap[url] = sourceFile;
  984.  
  985.         return sourceFile;
  986.     },
  987.  
  988.     getConstructorExpression: function(caller_frame, context)
  989.     {
  990.         // We believe we are just after the ctor call.
  991.         var decompiled_lineno = getLineAtPC(caller_frame, context);
  992.         var decompiled_lines = splitLines(caller_frame.script.functionSource);  // TODO place in sourceCache?
  993.         var candidate_line = decompiled_lines[decompiled_lineno - 1]; // zero origin
  994.         if (candidate_line && candidate_line != null)
  995.             {
  996.                 var m = reFunction.exec(candidate_line);
  997.                 if (m)
  998.                     var arguments =  m[1];     // TODO Lame: need to count parens, with escapes and quotes
  999.             }
  1000.         if (arguments) // need to break down commas and get last arg.
  1001.         {
  1002.                 var lastComma = arguments.lastIndexOf(',');
  1003.                 return arguments.substring(lastComma+1);  // if -1 then 0
  1004.         }
  1005.         return null;
  1006.     },
  1007.     // end of guilt trip
  1008.     // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  1009.  
  1010. // Called by debugger.onEval() to store eval() source.
  1011. // The frame has the blank-function-name script and it is not the top frame.
  1012. // The frame.script.fileName is given by spidermonkey as file of the first eval().
  1013. // The frame.script.baseLineNumber is given by spidermonkey as the line of the first eval() call
  1014. // The source that contains the eval() call is the source of our caller.
  1015. // If our caller is a file, the source of our caller is at frame.script.baseLineNumber
  1016. // If our caller is an eval, the source of our caller is TODO Check Test Case
  1017.  
  1018.     getEvalLevelSourceFile: function(frame, context, innerScripts)
  1019.     {
  1020.         var eval_expr = this.getEvalExpression(frame, context);
  1021.         var source  = this.getEvalBody(frame, "lib.getEvalLevelSourceFile.getEvalBody", 1, eval_expr);
  1022.         if (context.onReadySpy)  // coool we can get the request URL.
  1023.         {
  1024.             var url = context.onReadySpy.getURL();
  1025.         }
  1026.         else
  1027.             var url = this.getDynamicURL(frame, source, "eval");
  1028.  
  1029.         var lines = context.sourceCache.store(url, source);
  1030.         var sourceFile = new FBL.EvalLevelSourceFile(url, frame.script, eval_expr, lines, innerScripts);
  1031.         context.sourceFileMap[url] = sourceFile;
  1032.         return sourceFile;
  1033.     },
  1034.  
  1035.     getDynamicURL: function(frame, source, kind)
  1036.     {
  1037.         var url = this.getURLFromLastLine(source);
  1038.         if (url)
  1039.             url.kind = "source";
  1040.         else
  1041.         {
  1042.             var callerURL = normalizeURL(frame.script.fileName);
  1043.             var url = this.getURLFromMD5(callerURL, source, kind);
  1044.             if (url)
  1045.                 url.kind = "MD5";
  1046.             else
  1047.             {
  1048.                 var url = this.getDataURLForScript(frame.script, source);
  1049.                 url.kind = "data";
  1050.             }
  1051.         }
  1052.  
  1053.         return url;
  1054.     },
  1055.  
  1056.     getURLFromLastLine: function(source)
  1057.     {
  1058.         // Ignores any trailing whitespace in |source|
  1059.         const reURIinComment = /\/\/@\ssourceURL=\s*(\S*?)\s*$/m;
  1060.         var m = reURIinComment.exec(source);
  1061.         if (m)
  1062.             return m[1];
  1063.     },
  1064.  
  1065.     getURLFromMD5: function(callerURL, source, kind)
  1066.     {
  1067.         this.hash_service.init(this.nsICryptoHash.MD5);
  1068.         byteArray = [];
  1069.         for (var j = 0; j < source.length; j++)
  1070.         {
  1071.             byteArray.push( source.charCodeAt(j) );
  1072.         }
  1073.         this.hash_service.update(byteArray, byteArray.length);
  1074.         var hash = this.hash_service.finish(true);
  1075.  
  1076.         // encoding the hash should be ok, it should be information-preserving? Or at least reversable?
  1077.         var url = callerURL + (kind ? "/"+kind+"/" : "/") + encodeURIComponent(hash);
  1078.  
  1079.         return url;
  1080.     },
  1081.  
  1082.     getEvalExpression: function(frame, context)
  1083.     {
  1084.         var expr = this.getEvalExpressionFromEval(frame, context);  // eval in eval
  1085.  
  1086.         return (expr) ? expr : this.getEvalExpressionFromFile(normalizeURL(frame.script.fileName), frame.script.baseLineNumber, context);
  1087.     },
  1088.  
  1089.     getEvalExpressionFromFile: function(url, lineNo, context)
  1090.     {
  1091.         if (context && context.sourceCache)
  1092.         {
  1093.             var in_url = FBL.reJavascript.exec(url);
  1094.             if (in_url)
  1095.             {
  1096.                 var m = reEval.exec(in_url[1]);
  1097.                 if (m)
  1098.                     return m[1];
  1099.                 else
  1100.                     return null;
  1101.             }
  1102.  
  1103.             var htm = reHTM.exec(url);
  1104.             if (htm) {
  1105.                 lineNo = lineNo + 1; // embedded scripts seem to be off by one?  XXXjjb heuristic
  1106.             }
  1107.             // Walk backwards from the first line in the function until we find the line which
  1108.             // matches the pattern above, which is the eval call
  1109.             var line = "";
  1110.             for (var i = 0; i < 3; ++i)
  1111.             {
  1112.                 line = context.sourceCache.getLine(url, lineNo-i) + line;
  1113.                 if (line && line != null)
  1114.                 {
  1115.                     var m = reEval.exec(line);
  1116.                     if (m)
  1117.                         return m[1];
  1118.                 }
  1119.             }
  1120.         }
  1121.         return null;
  1122.     },
  1123.  
  1124.     getEvalExpressionFromEval: function(frame, context)
  1125.     {
  1126.         var callingFrame = frame.callingFrame;
  1127.         var sourceFile = FBL.getSourceFileByScript(context, callingFrame.script);
  1128.         if (sourceFile)
  1129.         {
  1130.             var lineNo = callingFrame.script.pcToLine(callingFrame.pc, PCMAP_SOURCETEXT);
  1131.             lineNo = lineNo - callingFrame.script.baseLineNumber + 1;
  1132.             var url  = sourceFile.href;
  1133.  
  1134.             var line = "";
  1135.             for (var i = 0; i < 3; ++i)
  1136.             {
  1137.                 line = context.sourceCache.getLine(url, lineNo-i) + line;
  1138.                 if (line && line != null)
  1139.                 {
  1140.                     var m = reEval.exec(line);
  1141.                     if (m)
  1142.                         return m[1];     // TODO Lame: need to count parens, with escapes and quotes
  1143.                 }
  1144.             }
  1145.         }
  1146.         return null;
  1147.     },
  1148.  
  1149.     getEvalBody: function(frame, asName, asLine, evalExpr)
  1150.     {
  1151.         if (evalExpr)
  1152.         {
  1153.             var result_src = {};
  1154.             var evalThis = "new String("+evalExpr+");";
  1155.             var evaled = frame.eval(evalThis, asName, asLine, result_src);
  1156.  
  1157.             if (evaled)
  1158.             {
  1159.                 var src = result_src.value.getWrappedValue();
  1160.                 return src;
  1161.             }
  1162.             else
  1163.             {
  1164.                 var source;
  1165.                 if(evalExpr == "function(p,a,c,k,e,r")
  1166.                     source = "/packer/ JS compressor detected";
  1167.                 else
  1168.                     source = frame.script.functionSource;
  1169.                 return source+" /* !eval("+evalThis+")) */";
  1170.             }
  1171.         }
  1172.         else
  1173.         {
  1174.             return frame.script.functionSource; // XXXms - possible crash on OSX
  1175.         }
  1176.     },
  1177.  
  1178.     getDataURLForScript: function(script, source)
  1179.     {
  1180.         if (!source)
  1181.             return "eval."+script.tag;
  1182.  
  1183.         // data:text/javascript;fileName=x%2Cy.js;baseLineNumber=10,<the-url-encoded-data>
  1184.         var uri = "data:text/javascript;";
  1185.         uri += "fileName="+encodeURIComponent(script.fileName) + ";";
  1186.         uri += "baseLineNumber="+encodeURIComponent(script.baseLineNumber) + ","
  1187.         uri += encodeURIComponent(source);
  1188.  
  1189.         return uri;
  1190.     },
  1191.  
  1192.     // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  1193.     // extends Module
  1194.  
  1195.     initialize: function()
  1196.     {
  1197.         this.nsICryptoHash = Components.interfaces["nsICryptoHash"];
  1198.  
  1199.         this.debuggerName =  window.location.href+"--"+FBL.getUniqueId(); /*@explore*/
  1200.         this.toString = function() { return this.debuggerName; } /*@explore*/
  1201.         this.hash_service = CCSV("@mozilla.org/security/hash;1", "nsICryptoHash");
  1202.  
  1203.         $("cmd_breakOnErrors").setAttribute("checked", Firebug.breakOnErrors);
  1204.         $("cmd_breakOnTopLevel").setAttribute("checked", Firebug.breakOnTopLevel);
  1205.  
  1206.         this.wrappedJSObject = this;  // how we communicate with fbs
  1207.         this.panelName = "script";
  1208.         this.description = $STR("script.modulemanager.description");
  1209.  
  1210.         // This is a service operation, a way of encapsulating fbs which is in turn implementing this
  1211.         // simple service. We could implment a whole component for this service, but it hardly makes sense.
  1212.         Firebug.broadcast = function encapsulateFBSBroadcast(message, args)
  1213.         {
  1214.             fbs.broadcast(message, args);
  1215.         }
  1216.  
  1217.         Firebug.ActivableModule.initialize.apply(this, arguments);
  1218.     },
  1219.  
  1220.     initializeUI: function()
  1221.     {
  1222.         Firebug.ActivableModule.initializeUI.apply(this, arguments);
  1223.         this.filterButton = $("fbScriptFilterMenu");
  1224.         this.filterMenuUpdate();
  1225.         fbs.registerClient(this);   // allow callbacks for jsd
  1226.         // 1.3.1 move fbs.registerDebugger(this);  // this will eventually set 'jsd' on the statusIcon
  1227.     },
  1228.  
  1229.     initContext: function(context)
  1230.     {
  1231.         Firebug.ActivableModule.initContext.apply(this, arguments);
  1232.     },
  1233.  
  1234.     reattachContext: function(browser, context)
  1235.     {
  1236.         var chrome = context ? context.chrome : FirebugChrome;
  1237.         this.filterButton = chrome.$("fbScriptFilterMenu");  // connect to the button in the new window, not 'window'
  1238.         this.filterMenuUpdate();
  1239.         Firebug.ActivableModule.reattachContext.apply(this, arguments);
  1240.     },
  1241.  
  1242.     loadedContext: function(context)
  1243.     {
  1244.         var watchPanel = this.ableWatchSidePanel(context);
  1245.         var needNow = watchPanel && watchPanel.watches;
  1246.         var watchPanelState = Firebug.getPanelState({name: "watches", context: context});
  1247.         var needPersistent = watchPanelState && watchPanelState.watches;
  1248.         if (needNow || needPersistent)
  1249.         {
  1250.             Firebug.CommandLine.isNeededGetReady(context);
  1251.             if (watchPanel)
  1252.             {
  1253.                 context.setTimeout(function refreshWatchesAfterCommandLineReady()
  1254.                 {
  1255.                     watchPanel.refresh();
  1256.                 });
  1257.             }
  1258.         }
  1259.         else
  1260.         {
  1261.         }
  1262.  
  1263.         updateScriptFiles(context);
  1264.  
  1265.         var panel = context.chrome.getSelectedPanel();
  1266.         if (panel && panel.name == "script" && panel.restoreRetry)
  1267.         {
  1268.             panel.location = null;  // the default could have been a URLOnly
  1269.             var state = Firebug.getPanelState(panel);
  1270.             panel.reShow(state);
  1271.             delete panel.restoreRetry;
  1272.         }
  1273.     },
  1274.  
  1275.     destroyContext: function(context, persistedState)
  1276.     {
  1277.         Firebug.ActivableModule.destroyContext.apply(this, arguments);
  1278.  
  1279.         if (context.stopped)
  1280.         {
  1281.             TabWatcher.cancelNextLoad = true;
  1282.             this.abort(context);
  1283.         }
  1284.     },
  1285.  
  1286.     updateOption: function(name, value)
  1287.     {
  1288.         if (name == "breakOnErrors")
  1289.             $("cmd_breakOnErrors").setAttribute("checked", value);
  1290.         else if (name == "breakOnTopLevel")
  1291.             $("cmd_breakOnTopLevel").setAttribute("checked", value);
  1292.     },
  1293.  
  1294.     getObjectByURL: function(context, url)
  1295.     {
  1296.         var sourceFile = getScriptFileByHref(url, context);
  1297.         if (sourceFile)
  1298.             return new SourceLink(sourceFile.href, 0, "js");
  1299.     },
  1300.  
  1301.     addListener: function(listener)
  1302.     {
  1303.         listeners.push(listener);
  1304.     },
  1305.  
  1306.     removeListener: function(listener)
  1307.     {
  1308.         remove(listeners, listener);
  1309.     },
  1310.  
  1311.     shutdown: function()
  1312.     {
  1313.         fbs.unregisterDebugger(this);
  1314.         fbs.unregisterClient(this);
  1315.     },
  1316.  
  1317.     registerDebugger: function() // 1.3.1 safe for multiple calls
  1318.     {
  1319.         if (this.registered)
  1320.             return;
  1321.         var check = fbs.registerDebugger(this);  //  this will eventually set 'jsd' on the statusIcon
  1322.         this.registered = true;
  1323.     },
  1324.  
  1325.     unregisterDebugger: function() // 1.3.1 safe for multiple calls
  1326.     {
  1327.         if (!this.registered)
  1328.             return;
  1329.         if (Firebug.Debugger.activeContexts.length > 0 || Firebug.Console.activeContexts.length > 0)
  1330.             return;  // don't turn off JSD unless both console and script panels are done.
  1331.         var check = fbs.unregisterDebugger(this);
  1332.         this.registered = false;
  1333.     },
  1334.  
  1335.     // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  1336.     // extends ActivableModule
  1337.     onFirstPanelActivate: function(context, init)
  1338.     {
  1339.         this.registerDebugger(); // 1.3.1
  1340.     },
  1341.  
  1342.     onPanelActivate: function(context, init, panelName)
  1343.     {
  1344.         //if (panelName == "console" || panelName == this.panelName)
  1345.         //    this.ableWatchSidePanel(context);
  1346.  
  1347.         if (panelName != this.panelName)
  1348.             return;
  1349.  
  1350.         if (!init)
  1351.             context.window.location.reload();
  1352.     },
  1353.  
  1354.     onPanelDeactivate: function(context, destroy, panelName)
  1355.     {
  1356.         if (panelName != this.panelName)
  1357.             return;
  1358.  
  1359.         if (!destroy)  // then the user is saying no to debugging
  1360.             this.clearAllBreakpoints(context);
  1361.         // else the context is being torn down, possibly to reload
  1362.     },
  1363.  
  1364.     onLastPanelDeactivate: function(context, destroy)
  1365.     {
  1366.         this.unregisterDebugger(); // 1.3.1
  1367.     },
  1368.  
  1369.     onSuspendFirebug: function(context)
  1370.     {
  1371.         fbs.pause();  // can be called multiple times.
  1372.         var active = this.setIsJSDActive();  // update ui
  1373.  
  1374.     },
  1375.  
  1376.     onResumeFirebug: function(context)
  1377.     {
  1378.         fbs.unPause();
  1379.         var active = this.setIsJSDActive();  // update ui
  1380.  
  1381.     },
  1382.  
  1383.     ableWatchSidePanel: function(context)
  1384.     {
  1385.         if (Firebug.Console.isEnabled(context))
  1386.         {
  1387.             var watchPanel = context.getPanel("watches", true);
  1388.             if (watchPanel)
  1389.                 watchPanel.enablePanel();
  1390.             return watchPanel;
  1391.         }
  1392.         else
  1393.         {
  1394.             var watchPanel = context.getPanel("watches", true);
  1395.             if (watchPanel)
  1396.                 watchPanel.disablePanel();
  1397.             return false;
  1398.         }
  1399.     },
  1400.  
  1401.     //---------------------------------------------------------------------------------------------
  1402.     // Menu in toolbar.
  1403.  
  1404.     onScriptFilterMenuTooltipShowing: function(tooltip, context)
  1405.     {
  1406.     },
  1407.  
  1408.     onScriptFilterMenuCommand: function(event, context)
  1409.     {
  1410.         var menu = event.target;
  1411.         Firebug.setPref(Firebug.servicePrefDomain, "scriptsFilter", menu.value);
  1412.         Firebug.Debugger.filterMenuUpdate();
  1413.     },
  1414.  
  1415.     menuFullLabel:
  1416.     {
  1417.         static: $STR("ScriptsFilterStatic"),
  1418.         evals: $STR("ScriptsFilterEval"),
  1419.         events: $STR("ScriptsFilterEvent"),
  1420.         all: $STR("ScriptsFilterAll"),
  1421.     },
  1422.  
  1423.     menuShortLabel:
  1424.     {
  1425.         static: $STR("ScriptsFilterStaticShort"),
  1426.         evals: $STR("ScriptsFilterEvalShort"),
  1427.         events: $STR("ScriptsFilterEventShort"),
  1428.         all: $STR("ScriptsFilterAllShort"),
  1429.     },
  1430.  
  1431.     onScriptFilterMenuPopupShowing: function(menu, context)
  1432.     {
  1433.         if (this.menuTooltip)
  1434.             this.menuTooltip.fbEnabled = false;
  1435.  
  1436.         var items = menu.getElementsByTagName("menuitem");
  1437.         var value = this.filterButton.value;
  1438.  
  1439.         for (var i=0; i<items.length; i++)
  1440.         {
  1441.             var option = items[i].value;
  1442.             if (!option)
  1443.                 continue;
  1444.  
  1445.             if (option == value)
  1446.                 items[i].setAttribute("checked", "true");
  1447.  
  1448.             items[i].label = Firebug.Debugger.menuFullLabel[option];
  1449.         }
  1450.  
  1451.         return true;
  1452.     },
  1453.  
  1454.     onScriptFilterMenuPopupHiding: function(tooltip, context)
  1455.     {
  1456.         if (this.menuTooltip)
  1457.             this.menuTooltip.fbEnabled = true;
  1458.  
  1459.         return true;
  1460.     },
  1461.  
  1462.     filterMenuUpdate: function()
  1463.     {
  1464.         var value = Firebug.getPref(Firebug.servicePrefDomain, "scriptsFilter");
  1465.         this.filterButton.value = value;
  1466.         this.filterButton.label = this.menuShortLabel[value];
  1467.         this.filterButton.removeAttribute("disabled");
  1468.         this.filterButton.setAttribute("value", value);
  1469.     },
  1470.  
  1471.     //----------------------------------------------------------------------------------
  1472.  
  1473. });
  1474.  
  1475.  
  1476. // ************************************************************************************************
  1477.  
  1478. Firebug.ScriptPanel = function() {};
  1479.  
  1480. Firebug.ScriptPanel.prototype = extend(Firebug.SourceBoxPanel,
  1481. {
  1482.  
  1483.     updateSourceBox: function(sourceBox)
  1484.     {
  1485.  
  1486.     },
  1487.  
  1488.     getSourceType: function()
  1489.     {
  1490.         return "js";
  1491.     },
  1492.  
  1493.     getDecorator: function(sourceBox)
  1494.     {
  1495.         if (!this.decorator)
  1496.             this.decorator = bind(this.decorateJavascript, this, sourceBox);
  1497.         return this.decorator;
  1498.     },
  1499.  
  1500.     decorateJavascript: function(sourceBox)
  1501.     {
  1502.         this.markExecutableLines(sourceBox);
  1503.         this.setLineBreakpoints(sourceBox.repObject, sourceBox)
  1504.     },
  1505.  
  1506.     showFunction: function(fn)
  1507.     {
  1508.         var sourceLink = findSourceForFunction(fn, this.context);
  1509.         if (sourceLink)
  1510.         {
  1511.             this.showSourceLink(sourceLink);
  1512.         }
  1513.         else
  1514.         {
  1515.         }
  1516.     },
  1517.  
  1518.     showSourceLink: function(sourceLink)
  1519.     {
  1520.         var sourceFile = getScriptFileByHref(sourceLink.href, this.context);
  1521.         if (sourceFile)
  1522.         {
  1523.             this.navigate(sourceFile);
  1524.             if (sourceLink.line)
  1525.                 this.scrollToLine(sourceLink.href, sourceLink.line, this.jumpHighlightFactory(sourceLink.line, this.context));
  1526.         }
  1527.     },
  1528.  
  1529.     showStackFrame: function(frame)
  1530.     {
  1531.         this.context.currentFrame = frame;
  1532.  
  1533.         if (!frame || (frame && !frame.isValid))
  1534.         {
  1535.             this.showNoStackFrame();
  1536.             return;
  1537.         }
  1538.  
  1539.         this.executionFile = FBL.getSourceFileByScript(this.context, frame.script);
  1540.         if (this.executionFile)
  1541.         {
  1542.             var url = this.executionFile.href;
  1543.             var analyzer = this.executionFile.getScriptAnalyzer(frame.script);
  1544.             this.executionLineNo = analyzer.getSourceLineFromFrame(this.context, frame);  // TODo implement for each type
  1545.             this.navigate(this.executionFile);
  1546.             this.scrollToLine(url, this.executionLineNo, bind(this.highlightExecutionLine, this) );
  1547.             this.context.throttle(this.updateInfoTip, this);
  1548.             return;
  1549.         }
  1550.         else
  1551.         {
  1552.             this.showNoStackFrame();
  1553.         }
  1554.     },
  1555.  
  1556.     showNoStackFrame: function()
  1557.     {
  1558.         this.executionFile = null;
  1559.         this.executionLineNo = -1;
  1560.         this.highlightExecutionLine(this.selectedSourceBox);
  1561.         this.updateInfoTip();
  1562.     },
  1563.  
  1564.     markExecutableLines: function(sourceBox)
  1565.     {
  1566.         var sourceFile = sourceBox.repObject;
  1567.         var lineNo = sourceBox.firstViewableLine;
  1568.         while( lineNode = sourceBox.getLineNode(lineNo) )
  1569.         {
  1570.             var script = sourceFile.scriptsIfLineCouldBeExecutable(lineNo, true);
  1571.  
  1572.             if (script)
  1573.                 lineNode.setAttribute("executable", "true");
  1574.             else
  1575.                 lineNode.removeAttribute("executable");
  1576.  
  1577.             lineNo++;
  1578.         }
  1579.     },
  1580.  
  1581.     highlightExecutionLine: function(sourceBox)
  1582.     {
  1583.         if (this.executionLine)  // could point to any node in any sourcebox
  1584.             this.executionLine.removeAttribute("exeLine");
  1585.  
  1586.         var lineNode = sourceBox.getLineNode(this.executionLineNo);
  1587.  
  1588.         this.executionLine = lineNode;  // if null, clears
  1589.  
  1590.         if (lineNode)
  1591.             lineNode.setAttribute("exeLine", "true");
  1592.                                                                                                                        /*@explore*/
  1593.         return true; // sticky
  1594.     },
  1595.  
  1596.     toggleBreakpoint: function(lineNo)
  1597.     {
  1598.         var lineNode = this.selectedSourceBox.getLineNode(lineNo);
  1599.         if (lineNode.getAttribute("breakpoint") == "true")
  1600.             fbs.clearBreakpoint(this.location.href, lineNo);
  1601.         else
  1602.             fbs.setBreakpoint(this.location, lineNo, null, Firebug.Debugger);
  1603.     },
  1604.  
  1605.     toggleDisableBreakpoint: function(lineNo)
  1606.     {
  1607.         var lineNode = this.selectedSourceBox.getLineNode(lineNo);
  1608.         if (lineNode.getAttribute("disabledBreakpoint") == "true")
  1609.             fbs.enableBreakpoint(this.location.href, lineNo);
  1610.         else
  1611.             fbs.disableBreakpoint(this.location.href, lineNo);
  1612.     },
  1613.  
  1614.     editBreakpointCondition: function(lineNo)
  1615.     {
  1616.         var sourceRow = this.selectedSourceBox.getLineNode(lineNo);
  1617.         var sourceLine = getChildByClass(sourceRow, "sourceLine");
  1618.         var condition = fbs.getBreakpointCondition(this.location.href, lineNo);
  1619.  
  1620.         Firebug.Editor.startEditing(sourceLine, condition);
  1621.     },
  1622.  
  1623.     // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  1624.  
  1625.     addSelectionWatch: function()
  1626.     {
  1627.         var watchPanel = this.context.getPanel("watches", true);
  1628.         if (watchPanel)
  1629.         {
  1630.             var selection = this.document.defaultView.getSelection().toString();
  1631.             watchPanel.addWatch(selection);
  1632.         }
  1633.     },
  1634.  
  1635.     // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  1636.  
  1637.     updateInfoTip: function()
  1638.     {
  1639.         var infoTip = this.panelBrowser.infoTip;
  1640.         if (infoTip && this.infoTipExpr)
  1641.             this.populateInfoTip(infoTip, this.infoTipExpr);
  1642.     },
  1643.  
  1644.     populateInfoTip: function(infoTip, expr)
  1645.     {
  1646.         if (!expr || isJavaScriptKeyword(expr))
  1647.             return false;
  1648.  
  1649.         var self = this;
  1650.         // If the evaluate fails, then we report an error and don't show the infoTip
  1651.         Firebug.CommandLine.evaluate(expr, this.context, null, this.context.window,
  1652.             function success(result, context)
  1653.             {
  1654.                 var rep = Firebug.getRep(result);
  1655.                 var tag = rep.shortTag ? rep.shortTag : rep.tag;
  1656.  
  1657.                 tag.replace({object: result}, infoTip);
  1658.  
  1659.                 self.infoTipExpr = expr;
  1660.             },
  1661.             function failed(result, context)
  1662.             {
  1663.                 self.infoTipExpr = "";
  1664.             }
  1665.         );
  1666.         return (self.infoTipExpr == expr);
  1667.     },
  1668.  
  1669.     // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  1670.     // UI event listeners
  1671.  
  1672.     onMouseDown: function(event)
  1673.     {
  1674.         var sourceLine = getAncestorByClass(event.target, "sourceLine");
  1675.         if (!sourceLine)
  1676.             return;
  1677.  
  1678.         var sourceRow = sourceLine.parentNode;
  1679.         var sourceFile = sourceRow.parentNode.repObject;
  1680.         var lineNo = parseInt(sourceLine.textContent);
  1681.  
  1682.         if (isLeftClick(event))
  1683.             this.toggleBreakpoint(lineNo);
  1684.         else if (isShiftClick(event))
  1685.             this.toggleDisableBreakpoint(lineNo);
  1686.         else if (isControlClick(event) || isMiddleClick(event))
  1687.         {
  1688.             Firebug.Debugger.runUntil(this.context, sourceFile, lineNo, Firebug.Debugger);
  1689.             cancelEvent(event);
  1690.         }
  1691.     },
  1692.  
  1693.     onContextMenu: function(event)
  1694.     {
  1695.         var sourceLine = getAncestorByClass(event.target, "sourceLine");
  1696.         if (!sourceLine)
  1697.             return;
  1698.  
  1699.         var lineNo = parseInt(sourceLine.textContent);
  1700.         this.editBreakpointCondition(lineNo);
  1701.         cancelEvent(event);
  1702.     },
  1703.  
  1704.     onMouseOver: function(event)
  1705.     {
  1706.         var sourceLine = getAncestorByClass(event.target, "sourceLine");
  1707.         if (sourceLine)
  1708.         {
  1709.             if (this.hoveredLine)
  1710.                 removeClass(this.hoveredLine.parentNode, "hovered");
  1711.  
  1712.             this.hoveredLine = sourceLine;
  1713.  
  1714.             if (sourceLine)
  1715.                 setClass(sourceLine.parentNode, "hovered");
  1716.         }
  1717.     },
  1718.  
  1719.     onMouseOut: function(event)
  1720.     {
  1721.         var sourceLine = getAncestorByClass(event.relatedTarget, "sourceLine");
  1722.         if (!sourceLine)
  1723.         {
  1724.             if (this.hoveredLine)
  1725.                 removeClass(this.hoveredLine.parentNode, "hovered");
  1726.  
  1727.             delete this.hoveredLine;
  1728.         }
  1729.     },
  1730.  
  1731.     onScroll: function(event)
  1732.     {
  1733.         var scrollingElement = event.target;
  1734.         this.reView(scrollingElement);
  1735.     },
  1736.  
  1737.     // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  1738.     // extends Panel
  1739.  
  1740.     name: "script",
  1741.     searchable: true,
  1742.  
  1743.     initialize: function(context, doc)
  1744.     {
  1745.         this.onMouseDown = bind(this.onMouseDown, this);
  1746.         this.onContextMenu = bind(this.onContextMenu, this);
  1747.         this.onMouseOver = bind(this.onMouseOver, this);
  1748.         this.onMouseOut = bind(this.onMouseOut, this);
  1749.         this.onScroll = bind(this.onScroll, this);
  1750.         this.setLineBreakpoints = bind(setLineBreakpoints, this);
  1751.  
  1752.         this.panelSplitter = $("fbPanelSplitter");
  1753.         this.sidePanelDeck = $("fbSidePanelDeck");
  1754.  
  1755.         Firebug.SourceBoxPanel.initialize.apply(this, arguments);
  1756.     },
  1757.  
  1758.     destroy: function(state)
  1759.     {
  1760.         delete this.selection; // We want the location (sourcefile) to persist, not the selection (eg stackFrame).
  1761.         persistObjects(this, state);
  1762.  
  1763.         var sourceBox = this.selectedSourceBox;
  1764.         state.lastScrollTop = sourceBox  && sourceBox.scrollTop
  1765.             ? sourceBox.scrollTop
  1766.             : this.lastScrollTop;
  1767.  
  1768.         delete this.selectedSourceBox;
  1769.  
  1770.         Firebug.SourceBoxPanel.destroy.apply(this, arguments);
  1771.     },
  1772.  
  1773.     detach: function(oldChrome, newChrome)
  1774.     {
  1775.         if (this.selectedSourceBox)
  1776.             this.lastSourceScrollTop = this.selectedSourceBox.scrollTop;
  1777.  
  1778.         if (this.context.stopped)
  1779.         {
  1780.             Firebug.Debugger.detachListeners(this.context, oldChrome);
  1781.             Firebug.Debugger.attachListeners(this.context, newChrome);
  1782.         }
  1783.  
  1784.         Firebug.Debugger.syncCommands(this.context);
  1785.  
  1786.         Firebug.SourceBoxPanel.detach.apply(this, arguments);
  1787.     },
  1788.  
  1789.     reattach: function(doc)
  1790.     {
  1791.         Firebug.SourceBoxPanel.reattach.apply(this, arguments);
  1792.  
  1793.         setTimeout(bind(function()
  1794.         {
  1795.             this.selectedSourceBox.scrollTop = this.lastSourceScrollTop;
  1796.             delete this.lastSourceScrollTop;
  1797.         }, this));
  1798.     },
  1799.  
  1800.     initializeNode: function(oldPanelNode)
  1801.     {
  1802.         this.tooltip = this.document.createElement("div");
  1803.         setClass(this.tooltip, "scriptTooltip");
  1804.         obscure(this.tooltip, true);
  1805.         this.panelNode.appendChild(this.tooltip);
  1806.  
  1807.         this.initializeSourceBoxes();
  1808.  
  1809.         this.panelNode.addEventListener("mousedown", this.onMouseDown, true);
  1810.         this.panelNode.addEventListener("contextmenu", this.onContextMenu, false);
  1811.         this.panelNode.addEventListener("mouseover", this.onMouseOver, false);
  1812.         this.panelNode.addEventListener("mouseout", this.onMouseOut, false);
  1813.         this.panelNode.addEventListener("scroll", this.onScroll, true);
  1814.     },
  1815.  
  1816.     destroyNode: function()
  1817.     {
  1818.         if (this.tooltipTimeout)
  1819.             clearTimeout(this.tooltipTimeout);
  1820.  
  1821.         this.panelNode.removeEventListener("mousedown", this.onMouseDown, true);
  1822.         this.panelNode.removeEventListener("contextmenu", this.onContextMenu, false);
  1823.         this.panelNode.removeEventListener("mouseover", this.onMouseOver, false);
  1824.         this.panelNode.removeEventListener("mouseout", this.onMouseOut, false);
  1825.         this.panelNode.removeEventListener("scroll", this.onScroll, true);
  1826.     },
  1827.  
  1828.     clear: function()
  1829.     {
  1830.         clearNode(this.panelNode);
  1831.     },
  1832.  
  1833.     show: function(state)
  1834.     {
  1835.         var enabled = Firebug.Debugger.isEnabled(this.context);
  1836.  
  1837.         // The "enable/disable" button is always visible.
  1838.         this.showToolbarButtons("fbScriptButtons", true);
  1839.  
  1840.         // static scripts can be shown
  1841.         this.showToolbarButtons("fbLocationList", true);
  1842.  
  1843.         // These buttons are visible only if debugger is enabled.
  1844.         this.showToolbarButtons("fbLocationSeparator", enabled);
  1845.         this.showToolbarButtons("fbDebuggerButtons", enabled);
  1846.  
  1847.         this.obeyPreferences();
  1848.  
  1849.         // Additional debugger panels are visible only if debugger
  1850.         // is enabled.
  1851.         this.panelSplitter.collapsed = !enabled;
  1852.         this.sidePanelDeck.collapsed = !enabled;
  1853.  
  1854.         this.reShow(state);
  1855.     },
  1856.  
  1857.     reShow: function(state)
  1858.     {
  1859.         this.retryRestore = restoreObjects(this, state, true);  // delay the retry for loadedContext
  1860.  
  1861.         if (state)  // then we are restoring
  1862.         {
  1863.             this.context.throttle(function()
  1864.             {
  1865.                 var sourceBox = this.selectedSourceBox;
  1866.                 if (sourceBox)
  1867.                     sourceBox.scrollTop = state.lastScrollTop;
  1868.             }, this);
  1869.         }
  1870.  
  1871.         var enabled = Firebug.Debugger.isEnabled(this.context);
  1872.         if (enabled)
  1873.         {
  1874.             Firebug.ModuleManagerPage.hide(this); // the navigate in restoreObject remains in effect
  1875.  
  1876.             var breakpointPanel = this.context.getPanel("breakpoints", true);
  1877.             if (breakpointPanel)
  1878.                 breakpointPanel.refresh();
  1879.         }
  1880.         else
  1881.         {
  1882.             // xxxHonza: ModuleManagerPage should be always displayed if
  1883.             // debugger is disabled.
  1884.             //if (!state.persistedLocation)
  1885.                 Firebug.ModuleManagerPage.show(this, Firebug.Debugger);
  1886.             // else the navigate in restoreObject remains in effect
  1887.         }
  1888.     },
  1889.  
  1890.     obeyPreferences: function()
  1891.     {
  1892.         if (Firebug.omitObjectPathStack)  // User does not want the toolbar stack
  1893.             FBL.hide(panelStatus, true);
  1894.     },
  1895.  
  1896.     hide: function(state)
  1897.     {
  1898.         this.showToolbarButtons("fbDebuggerButtons", false);
  1899.         this.showToolbarButtons("fbScriptButtons", false);
  1900.         FBL.hide(panelStatus, false);
  1901.  
  1902.         delete this.infoTipExpr;
  1903.  
  1904.         var sourceBox = this.selectedSourceBox;
  1905.         if (sourceBox && state)
  1906.             state.lastScrollTop = sourceBox.scrollTop;
  1907.     },
  1908.  
  1909.     search: function(text)
  1910.     {
  1911.         var sourceBox = this.selectedSourceBox;
  1912.         if (!text || !sourceBox)
  1913.         {
  1914.             delete this.currentSearch;
  1915.             return false;
  1916.         }
  1917.  
  1918.         // Check if the search is for a line number
  1919.         var m = reLineNumber.exec(text);
  1920.         if (m)
  1921.         {
  1922.             if (!m[1])
  1923.                 return true; // Don't beep if only a # has been typed
  1924.  
  1925.             var lineNo = parseInt(m[1]);
  1926.             if (!isNaN(lineNo) && (lineNo > 0) && (lineNo < sourceBox.lines.length) )
  1927.             {
  1928.                 this.scrollToLine(sourceBox.repObject.href, lineNo,  this.jumpHighlightFactory(lineNo, this.context))
  1929.                 return true;
  1930.             }
  1931.         }
  1932.  
  1933.         var lineNo = null;
  1934.         if (this.currentSearch && text == this.currentSearch.text)
  1935.             lineNo = this.currentSearch.findNext(true);
  1936.         else
  1937.         {
  1938.             this.currentSearch = new SourceBoxTextSearch(sourceBox);
  1939.             lineNo = this.currentSearch.find(text);
  1940.         }
  1941.  
  1942.         if (lineNo)
  1943.         {
  1944.             // this lineNo is an zero-based index into sourceBox.lines. Add one for user line numbers
  1945.             this.scrollToLine(sourceBox.repObject.href, lineNo, this.jumpHighlightFactory(lineNo+1, this.context));
  1946.             //var sel = this.document.defaultView.getSelection();
  1947.             //sel.removeAllRanges();
  1948.             //sel.addRange(this.currentSearch.range);
  1949.  
  1950.             //scrollIntoCenterView(row, sourceBox);
  1951.             return true;
  1952.         }
  1953.         else
  1954.             return false;
  1955.     },
  1956.  
  1957.     supportsObject: function(object)
  1958.     {
  1959.         if( object instanceof jsdIStackFrame
  1960.             || object instanceof SourceFile
  1961.             || (object instanceof SourceLink && object.type == "js")
  1962.             || typeof(object) == "function" )
  1963.             return 1;
  1964.         else return 0;
  1965.     },
  1966.  
  1967.     updateLocation: function(sourceFile)
  1968.     {
  1969.         if (!Firebug.Debugger.isEnabled(this.context))
  1970.             Firebug.ModuleManagerPage.hide(this);
  1971.  
  1972.         // Since our last use of the sourceFile we may have compiled or recompiled the source
  1973.         var updatedSourceFile = this.context.sourceFileMap[sourceFile.href];
  1974.         if (!updatedSourceFile)
  1975.             updatedSourceFile = this.getDefaultLocation(this.context);
  1976.         if (!updatedSourceFile)
  1977.             return;
  1978.  
  1979.         this.showSourceFile(updatedSourceFile);
  1980.     },
  1981.  
  1982.     updateSelection: function(object)
  1983.     {
  1984.         if (object instanceof jsdIStackFrame)
  1985.             this.showStackFrame(object);
  1986.         else if (object instanceof SourceFile)
  1987.             this.navigate(object);
  1988.         else if (object instanceof SourceLink)
  1989.             this.showSourceLink(object);
  1990.         else if (typeof(object) == "function")
  1991.             this.showFunction(object);
  1992.         else
  1993.             this.showStackFrame(null);
  1994.     },
  1995.  
  1996.     showThisSourceFile: function(sourceFile)
  1997.     {
  1998.         //-----------------------------------123456789
  1999.         if (sourceFile.href.substr(0, 9) == "chrome://")
  2000.             return false;
  2001.  
  2002.            if (sourceFile.isEval() && !this.showEvals)
  2003.                return false;
  2004.  
  2005.         if (sourceFile.isEvent() && !this.showEvents)
  2006.             return false;
  2007.  
  2008.         return true;
  2009.     },
  2010.  
  2011.     getLocationList: function()
  2012.     {
  2013.         var context = this.context;
  2014.         var allSources = sourceFilesAsArray(context);
  2015.  
  2016.         if (Firebug.showAllSourceFiles)
  2017.         {
  2018.             return allSources;
  2019.         }
  2020.  
  2021.         var filter = Firebug.getPref(Firebug.servicePrefDomain, "scriptsFilter");
  2022.         this.showEvents = (filter == "all" || filter == "events");
  2023.         this.showEvals = (filter == "all" || filter == "evals");
  2024.  
  2025.         var list = [];
  2026.         for (var i = 0; i < allSources.length; i++)
  2027.         {
  2028.             if (this.showThisSourceFile(allSources[i]))
  2029.                 list.push(allSources[i]);
  2030.         }
  2031.  
  2032.        iterateWindows(context.window, function(win) {
  2033.             if (!win.document.documentElement)
  2034.                 return;
  2035.             var url = win.location.href;
  2036.             if (url)
  2037.             {
  2038.                 if (context.sourceFileMap.hasOwnProperty(url))
  2039.                     return;
  2040.                 var URLOnly = new NoScriptSourceFile(context, url);
  2041.                 context.sourceFileMap[url] = URLOnly;
  2042.                 list.push(URLOnly);
  2043.             }
  2044.         });
  2045.         return list;
  2046.     },
  2047.  
  2048.     getDefaultLocation: function(context)
  2049.     {
  2050.         var sourceFiles = this.getLocationList();
  2051.         if (context)
  2052.         {
  2053.             var url = context.window.location.toString();
  2054.             for (var i = 0; i < sourceFiles.length; i++)
  2055.             {
  2056.                 if (url == sourceFiles[i].href)
  2057.                     return sourceFiles[i];
  2058.             }
  2059.             return sourceFiles[0];
  2060.         }
  2061.         else
  2062.             return sourceFiles[0];
  2063.     },
  2064.  
  2065.     getDefaultSelection: function(context)
  2066.     {
  2067.         return this.getDefaultLocation(context);
  2068.     },
  2069.  
  2070.     getTooltipObject: function(target)
  2071.     {
  2072.         // Target should be A element with class = sourceLine
  2073.         if ( hasClass(target, 'sourceLine') )
  2074.         {
  2075.             var lineNo = parseInt(target.innerHTML);
  2076.  
  2077.             if ( isNaN(lineNo) )
  2078.                 return;
  2079.             var scripts = this.location.scriptsIfLineCouldBeExecutable(lineNo);
  2080.             if (scripts)
  2081.             {
  2082.                 var str = "scripts ";
  2083.                 for(var i = 0; i < scripts.length; i++)
  2084.                     str += scripts[i].tag +" ";
  2085.                 return str;
  2086.             }
  2087.             else
  2088.                 return new String("no executable script at "+lineNo);
  2089.         }
  2090.         return null;
  2091.     },
  2092.  
  2093.     getPopupObject: function(target)
  2094.     {
  2095.         // Don't show popup over the line numbers, we show the conditional breakpoint
  2096.         // editor there instead
  2097.         var sourceLine = getAncestorByClass(target, "sourceLine");
  2098.         if (sourceLine)
  2099.             return;
  2100.  
  2101.         var sourceRow = getAncestorByClass(target, "sourceRow");
  2102.         if (!sourceRow)
  2103.             return;
  2104.  
  2105.         var lineNo = parseInt(sourceRow.firstChild.textContent);
  2106.         var scripts = findScripts(this.context, this.location.href, lineNo);
  2107.         return scripts; // gee I wonder what will happen?
  2108.     },
  2109.  
  2110.     showInfoTip: function(infoTip, target, x, y, rangeParent, rangeOffset)
  2111.     {
  2112.         var frame = this.context.currentFrame;
  2113.         if (!frame)
  2114.             return;
  2115.  
  2116.         var sourceRowText = getAncestorByClass(target, "sourceRowText");
  2117.         if (!sourceRowText)
  2118.             return;
  2119.  
  2120.         // see http://code.google.com/p/fbug/issues/detail?id=889
  2121.         // idea from: Jonathan Zarate's rikaichan extension (http://www.polarcloud.com/rikaichan/)
  2122.         if (!rangeParent)
  2123.             return;
  2124.         rangeOffset = rangeOffset || 0;
  2125.         var expr = getExpressionAt(rangeParent.data, rangeOffset);
  2126.         if (!expr || !expr.expr)
  2127.             return;
  2128.  
  2129.         if (expr.expr == this.infoTipExpr)
  2130.             return true;
  2131.         else
  2132.             return this.populateInfoTip(infoTip, expr.expr);
  2133.     },
  2134.  
  2135.     getObjectPath: function(frame)
  2136.     {
  2137.         frame = this.context.debugFrame;
  2138.  
  2139.         var frames = [];
  2140.         for (; frame; frame = getCallingFrame(frame))
  2141.             frames.push(frame);
  2142.  
  2143.         return frames;
  2144.     },
  2145.  
  2146.     getObjectLocation: function(sourceFile)
  2147.     {
  2148.         return sourceFile.href;
  2149.     },
  2150.  
  2151.     // return.path: group/category label, return.name: item label
  2152.     getObjectDescription: function(sourceFile)
  2153.     {
  2154.         return sourceFile.getObjectDescription();
  2155.     },
  2156.  
  2157.     getOptionsMenuItems: function()
  2158.     {
  2159.         var context = this.context;
  2160.  
  2161.         return [
  2162.             serviceOptionMenu("BreakOnAllErrors", "breakOnErrors"),
  2163.             // wait 1.2 optionMenu("BreakOnTopLevel", "breakOnTopLevel"),
  2164.             serviceOptionMenu("ShowAllSourceFiles", "showAllSourceFiles"),
  2165.             // 1.2: always check last line; optionMenu("UseLastLineForEvalName", "useLastLineForEvalName"),
  2166.             // 1.2: always use MD5 optionMenu("UseMD5ForEvalName", "useMD5ForEvalName")
  2167.             serviceOptionMenu("TrackThrowCatch", "trackThrowCatch"),
  2168.             //"-",
  2169.             //1.2 option on toolbar this.optionMenu("DebuggerEnableAlways", enableAlwaysPref)
  2170.         ];
  2171.     },
  2172.  
  2173.     optionMenu: function(label, option)
  2174.     {
  2175.         var checked = Firebug.getPref(prefDomain, option);
  2176.         return {label: label, type: "checkbox", checked: checked,
  2177.             command: bindFixed(Firebug.setPref, Firebug, prefDomain, option, !checked) };
  2178.     },
  2179.  
  2180.     getContextMenuItems: function(fn, target)
  2181.     {
  2182.         if (getAncestorByClass(target, "sourceLine"))
  2183.             return;
  2184.  
  2185.         var sourceRow = getAncestorByClass(target, "sourceRow");
  2186.         if (!sourceRow)
  2187.             return;
  2188.  
  2189.         var sourceLine = getChildByClass(sourceRow, "sourceLine");
  2190.         var lineNo = parseInt(sourceLine.textContent);
  2191.  
  2192.         var items = [];
  2193.  
  2194.         var selection = this.document.defaultView.getSelection();
  2195.         if (selection.toString())
  2196.         {
  2197.             items.push(
  2198.                 "-",
  2199.                 {label: "AddWatch", command: bind(this.addSelectionWatch, this) }
  2200.             );
  2201.         }
  2202.  
  2203.         var hasBreakpoint = sourceRow.getAttribute("breakpoint") == "true";
  2204.  
  2205.         items.push(
  2206.             "-",
  2207.             {label: "SetBreakpoint", type: "checkbox", checked: hasBreakpoint,
  2208.                 command: bindFixed(this.toggleBreakpoint, this, lineNo) }
  2209.         );
  2210.         if (hasBreakpoint)
  2211.         {
  2212.             var isDisabled = fbs.isBreakpointDisabled(this.location.href, lineNo);
  2213.             items.push(
  2214.                 {label: "DisableBreakpoint", type: "checkbox", checked: isDisabled,
  2215.                     command: bindFixed(this.toggleDisableBreakpoint, this, lineNo) }
  2216.             );
  2217.         }
  2218.         items.push(
  2219.             {label: "EditBreakpointCondition",
  2220.                 command: bindFixed(this.editBreakpointCondition, this, lineNo) }
  2221.         );
  2222.  
  2223.         if (this.context.stopped)
  2224.         {
  2225.             var sourceRow = getAncestorByClass(target, "sourceRow");
  2226.             if (sourceRow)
  2227.             {
  2228.                 var sourceFile = sourceRow.parentNode.repObject;
  2229.                 var lineNo = parseInt(sourceRow.firstChild.textContent);
  2230.  
  2231.                 var debuggr = Firebug.Debugger;
  2232.                 items.push(
  2233.                     "-",
  2234.                     {label: "Continue",
  2235.                         command: bindFixed(debuggr.resume, debuggr, this.context) },
  2236.                     {label: "StepOver",
  2237.                         command: bindFixed(debuggr.stepOver, debuggr, this.context) },
  2238.                     {label: "StepInto",
  2239.                         command: bindFixed(debuggr.stepInto, debuggr, this.context) },
  2240.                     {label: "StepOut",
  2241.                         command: bindFixed(debuggr.stepOut, debuggr, this.context) },
  2242.                     {label: "RunUntil",
  2243.                         command: bindFixed(debuggr.runUntil, debuggr, this.context,
  2244.                         sourceFile, lineNo) }
  2245.                 );
  2246.             }
  2247.         }
  2248.  
  2249.         return items;
  2250.     },
  2251.  
  2252.     getEditor: function(target, value)
  2253.     {
  2254.         if (!this.conditionEditor)
  2255.             this.conditionEditor = new ConditionEditor(this.document);
  2256.  
  2257.         return this.conditionEditor;
  2258.     },
  2259.  
  2260. });
  2261.  
  2262. // ************************************************************************************************
  2263.  
  2264. var BreakpointsTemplate = domplate(Firebug.Rep,
  2265. {
  2266.     tag:
  2267.         DIV({onclick: "$onClick"},
  2268.             FOR("group", "$groups",
  2269.                 DIV({class: "breakpointBlock breakpointBlock-$group.name"},
  2270.                     H1({class: "breakpointHeader groupHeader"},
  2271.                         "$group.title"
  2272.                     ),
  2273.                     FOR("bp", "$group.breakpoints",
  2274.                         DIV({class: "breakpointRow"},
  2275.                             DIV({class: "breakpointBlockHead"},
  2276.                                 INPUT({class: "breakpointCheckbox", type: "checkbox",
  2277.                                     _checked: "$bp.checked"}),
  2278.                                 SPAN({class: "breakpointName"}, "$bp.name"),
  2279.                                 TAG(FirebugReps.SourceLink.tag, {object: "$bp|getSourceLink"}),
  2280.                                 IMG({class: "closeButton", src: "blank.gif"})
  2281.                             ),
  2282.                             DIV({class: "breakpointCode"}, "$bp.sourceLine")
  2283.                         )
  2284.                     )
  2285.                 )
  2286.             )
  2287.         ),
  2288.  
  2289.     getSourceLink: function(bp)
  2290.     {
  2291.         return new SourceLink(bp.href, bp.lineNumber, "js");
  2292.     },
  2293.  
  2294.     onClick: function(event)
  2295.     {
  2296.         var panel = Firebug.getElementPanel(event.target);
  2297.  
  2298.         if (getAncestorByClass(event.target, "breakpointCheckbox"))
  2299.         {
  2300.             var sourceLink =
  2301.                 getElementByClass(event.target.parentNode, "objectLink-sourceLink").repObject;
  2302.  
  2303.             panel.noRefresh = true;
  2304.             if (event.target.checked)
  2305.                 fbs.enableBreakpoint(sourceLink.href, sourceLink.line);
  2306.             else
  2307.                 fbs.disableBreakpoint(sourceLink.href, sourceLink.line);
  2308.             panel.noRefresh = false;
  2309.         }
  2310.         else if (getAncestorByClass(event.target, "closeButton"))
  2311.         {
  2312.             var sourceLink =
  2313.                 getElementByClass(event.target.parentNode, "objectLink-sourceLink").repObject;
  2314.  
  2315.             panel.noRefresh = true;
  2316.  
  2317.             var head = getAncestorByClass(event.target, "breakpointBlock");
  2318.             var groupName = getClassValue(head, "breakpointBlock");
  2319.             if (groupName == "breakpoints")
  2320.                 fbs.clearBreakpoint(sourceLink.href, sourceLink.line);
  2321.             else if (groupName == "errorBreakpoints")
  2322.                 fbs.clearErrorBreakpoint(sourceLink.href, sourceLink.line);
  2323.             else if (groupName == "monitors")
  2324.             {
  2325.                 fbs.unmonitor(sourceLink.href, sourceLink.line)
  2326.             }
  2327.  
  2328.             var row = getAncestorByClass(event.target, "breakpointRow");
  2329.             panel.removeRow(row);
  2330.  
  2331.             panel.noRefresh = false;
  2332.         }
  2333.     }
  2334. });
  2335.  
  2336. // ************************************************************************************************
  2337.  
  2338. function BreakpointsPanel() {}
  2339.  
  2340. BreakpointsPanel.prototype = extend(Firebug.Panel,
  2341. {
  2342.     removeRow: function(row)
  2343.     {
  2344.         row.parentNode.removeChild(row);
  2345.  
  2346.         var bpCount = countBreakpoints(this.context);
  2347.         if (!bpCount)
  2348.             this.refresh();
  2349.     },
  2350.  
  2351.     // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  2352.     // extends Panel
  2353.  
  2354.     name: "breakpoints",
  2355.     parentPanel: "script",
  2356.     order: 2,
  2357.  
  2358.     initialize: function()
  2359.     {
  2360.         Firebug.Panel.initialize.apply(this, arguments);
  2361.     },
  2362.  
  2363.     destroy: function(state)
  2364.     {
  2365.         Firebug.Panel.destroy.apply(this, arguments);
  2366.     },
  2367.  
  2368.     show: function(state)
  2369.     {
  2370.         this.refresh();
  2371.     },
  2372.  
  2373.     refresh: function()
  2374.     {
  2375.         updateScriptFiles(this.context);
  2376.  
  2377.         var breakpoints = [];
  2378.         var errorBreakpoints = [];
  2379.         var monitors = [];
  2380.  
  2381.         var context = this.context;
  2382.  
  2383.         for (var url in this.context.sourceFileMap)
  2384.         {
  2385.             fbs.enumerateBreakpoints(url, {call: function(url, line, script, props)
  2386.             {
  2387.                 if (script)  // then this is a current (not future) breakpoint
  2388.                 {
  2389.                     var analyzer = getScriptAnalyzer(context, script);
  2390.                     if (analyzer)
  2391.                         var name = analyzer.getFunctionDescription(script, context).name;
  2392.                     else
  2393.                         var name = guessFunctionName(url, 1, context);
  2394.                     var isFuture = false;
  2395.                 }
  2396.                 else
  2397.                 {
  2398.                     var isFuture = true;
  2399.                 }
  2400.  
  2401.                 var source = context.sourceCache.getLine(url, line);
  2402.                 breakpoints.push({name : name, href: url, lineNumber: line,
  2403.                     checked: !props.disabled, sourceLine: source, isFuture: isFuture});
  2404.             }});
  2405.  
  2406.             fbs.enumerateErrorBreakpoints(url, {call: function(url, line)
  2407.             {
  2408.                 var name = guessEnclosingFunctionName(url, line);
  2409.                 var source = context.sourceCache.getLine(url, line);
  2410.                 errorBreakpoints.push({name: name, href: url, lineNumber: line, checked: true,
  2411.                     sourceLine: source});
  2412.             }});
  2413.  
  2414.             fbs.enumerateMonitors(url, {call: function(url, line)
  2415.             {
  2416.                 var name = guessEnclosingFunctionName(url, line);
  2417.                 monitors.push({name: name, href: url, lineNumber: line, checked: true,
  2418.                         sourceLine: ""});
  2419.             }});
  2420.         }
  2421.  
  2422.         function sortBreakpoints(a, b)
  2423.         {
  2424.             if (a.href == b.href)
  2425.                 return a.lineNumber < b.lineNumber ? -1 : 1;
  2426.             else
  2427.                 return a.href < b.href ? -1 : 1;
  2428.         }
  2429.  
  2430.         breakpoints.sort(sortBreakpoints);
  2431.         errorBreakpoints.sort(sortBreakpoints);
  2432.         monitors.sort(sortBreakpoints);
  2433.  
  2434.         var groups = [];
  2435.  
  2436.         if (breakpoints.length)
  2437.             groups.push({name: "breakpoints", title: $STR("Breakpoints"),
  2438.                 breakpoints: breakpoints});
  2439.         if (errorBreakpoints.length)
  2440.             groups.push({name: "errorBreakpoints", title: $STR("ErrorBreakpoints"),
  2441.                 breakpoints: errorBreakpoints});
  2442.         if (monitors.length)
  2443.             groups.push({name: "monitors", title: $STR("LoggedFunctions"),
  2444.                 breakpoints: monitors});
  2445.  
  2446.         if (groups.length)
  2447.             BreakpointsTemplate.tag.replace({groups: groups}, this.panelNode);
  2448.         else
  2449.             FirebugReps.Warning.tag.replace({object: "NoBreakpointsWarning"}, this.panelNode);
  2450.  
  2451.     },
  2452.  
  2453.     getOptionsMenuItems: function()
  2454.     {
  2455.         var items = [];
  2456.  
  2457.         var context = this.context;
  2458.         updateScriptFiles(context);
  2459.  
  2460.         var bpCount = 0, disabledCount = 0;
  2461.         for (var url in context.sourceFileMap)
  2462.         {
  2463.             fbs.enumerateBreakpoints(url, {call: function(url, line, script, disabled, condition)
  2464.             {
  2465.                 ++bpCount;
  2466.                 if (fbs.isBreakpointDisabled(url, line))
  2467.                     ++disabledCount;
  2468.             }});
  2469.         }
  2470.  
  2471.         if (disabledCount)
  2472.         {
  2473.             items.push(
  2474.                 {label: "EnableAllBreakpoints",
  2475.                     command: bindFixed(
  2476.                         Firebug.Debugger.enableAllBreakpoints, Firebug.Debugger, context) }
  2477.             );
  2478.         }
  2479.         if (bpCount && disabledCount != bpCount)
  2480.         {
  2481.             items.push(
  2482.                 {label: "DisableAllBreakpoints",
  2483.                     command: bindFixed(
  2484.                         Firebug.Debugger.disableAllBreakpoints, Firebug.Debugger, context) }
  2485.             );
  2486.         }
  2487.  
  2488.         items.push(
  2489.             "-",
  2490.             {label: "ClearAllBreakpoints", disabled: !bpCount,
  2491.                 command: bindFixed(Firebug.Debugger.clearAllBreakpoints, Firebug.Debugger, context) }
  2492.         );
  2493.  
  2494.         return items;
  2495.     },
  2496.  
  2497. });
  2498.  
  2499. Firebug.DebuggerListener =
  2500. {
  2501.     onJSDActivate: function(jsd)  // start or unPause
  2502.     {
  2503.  
  2504.     },
  2505.     onJSDDeactivate: function(jsd) // stop or pause
  2506.     {
  2507.  
  2508.     },
  2509.     onStop: function(context, frame, type, rv)
  2510.     {
  2511.     },
  2512.  
  2513.     onResume: function(context)
  2514.     {
  2515.     },
  2516.  
  2517.     onThrow: function(context, frame, rv)
  2518.     {
  2519.         return false; /* continue throw */
  2520.     },
  2521.  
  2522.     onError: function(context, frame, error)
  2523.     {
  2524.     },
  2525.  
  2526.     onEventScriptCreated: function(context, frame, url)
  2527.     {
  2528.     },
  2529.  
  2530.     onTopLevelScriptCreated: function(context, frame, url)
  2531.     {
  2532.     },
  2533.  
  2534.     onEvalScriptCreated: function(context, frame, url)
  2535.     {
  2536.     },
  2537.  
  2538.     onFunctionConstructor: function(context, frame, ctor_script, url)
  2539.     {
  2540.     },
  2541. };
  2542.  
  2543. // ************************************************************************************************
  2544.  
  2545. function CallstackPanel() { }
  2546.  
  2547. CallstackPanel.prototype = extend(Firebug.Panel,
  2548. {
  2549.     // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  2550.     // extends Panel
  2551.  
  2552.     name: "callstack",
  2553.     parentPanel: "script",
  2554.     order: 1,
  2555.  
  2556.     initialize: function(context, doc)
  2557.     {
  2558.         Firebug.Panel.initialize.apply(this, arguments);
  2559.     },
  2560.  
  2561.     destroy: function(state)
  2562.     {
  2563.         Firebug.Panel.destroy.apply(this, arguments);
  2564.     },
  2565.  
  2566.     show: function(state)
  2567.     {
  2568.           this.refresh();
  2569.     },
  2570.  
  2571.     supportsObject: function(object)
  2572.     {
  2573.         return object instanceof jsdIStackFrame;
  2574.     },
  2575.  
  2576.     updateSelection: function(object)
  2577.     {
  2578.         if (object instanceof jsdIStackFrame)
  2579.             this.highlightFrame(object);
  2580.     },
  2581.  
  2582.     refresh: function()
  2583.     {
  2584.         var mainPanel = this.context.getPanel("script", true);
  2585.         if (mainPanel.selection instanceof jsdIStackFrame)
  2586.             this.showStackFrame(mainPanel.selection);
  2587.     },
  2588.  
  2589.     showStackFrame: function(frame)
  2590.     {
  2591.         clearNode(this.panelNode);
  2592.         var mainPanel = this.context.getPanel("script", true);
  2593.  
  2594.         if (mainPanel && frame)
  2595.         {
  2596.             FBL.setClass(this.panelNode, "objectBox-stackTrace");
  2597.             // The panelStatus has the stack, lets reuse it to give the same UX as that control.
  2598.             var labels = panelStatus.getElementsByTagName("label");
  2599.             var doc = this.panelNode.ownerDocument;
  2600.             for (var i = 0; i < labels.length; i++)
  2601.             {
  2602.                 if (FBL.hasClass(labels[i], "panelStatusLabel"))
  2603.                 {
  2604.                     var div = doc.createElement("div");
  2605.                     var label = labels[i];
  2606.                     div.innerHTML = label.getAttribute('value');
  2607.                     if (label.repObject instanceof jsdIStackFrame)  // causes a downcast
  2608.                         div.frame = label.repObject;
  2609.                     div.label = label;
  2610.                     FBL.setClass(div, "objectLink");
  2611.                     FBL.setClass(div, "objectLink-stackFrame");
  2612.  
  2613.                     div.addEventListener("click", function(event)
  2614.                     {
  2615.                         var revent = document.createEvent("MouseEvents");
  2616.                         revent.initMouseEvent("mousedown", true, true, window,
  2617.                                 0, 0, 0, 0, 0, false, false, false, false, 0, null);
  2618.                         event.target.label.dispatchEvent(revent);
  2619.                     }, false);
  2620.                     this.panelNode.appendChild(div);
  2621.                 }
  2622.             }
  2623.         }
  2624.     },
  2625.  
  2626.     highlightFrame: function(frame)
  2627.     {
  2628.         var frameViews = this.panelNode.childNodes
  2629.         for (var child = this.panelNode.firstChild; child; child = child.nextSibling)
  2630.         {
  2631.             if (child.label && child.label.repObject && child.label.repObject == frame)
  2632.             {
  2633.                 this.selectItem(child);
  2634.                 return true;
  2635.             }
  2636.         }
  2637.         return false;
  2638.     },
  2639.  
  2640.     selectItem: function(item)
  2641.     {
  2642.         if (this.selectedItem)
  2643.             this.selectedItem.removeAttribute("selected");
  2644.  
  2645.         this.selectedItem = item;
  2646.  
  2647.         if (item)
  2648.             item.setAttribute("selected", "true");
  2649.     },
  2650.  
  2651.     getOptionsMenuItems: function()
  2652.     {
  2653.         var items = [
  2654.             optionMenu("OmitObjectPathStack", "omitObjectPathStack"),
  2655.             ];
  2656.         return items;
  2657.     }
  2658. });
  2659.  
  2660. // ************************************************************************************************
  2661. // Local Helpers
  2662.  
  2663. function ConditionEditor(doc)
  2664. {
  2665.     this.box = this.tag.replace({}, doc, this);
  2666.     this.input = this.box.childNodes[1].firstChild.firstChild.lastChild;  // XXXjjb we need childNode[1] always
  2667.     this.initialize();
  2668. }
  2669.  
  2670. ConditionEditor.prototype = domplate(Firebug.InlineEditor.prototype,
  2671. {
  2672.     tag:
  2673.         DIV({class: "conditionEditor"},
  2674.             DIV({class: "conditionEditorTop1"},
  2675.                 DIV({class: "conditionEditorTop2"})
  2676.             ),
  2677.             DIV({class: "conditionEditorInner1"},
  2678.                 DIV({class: "conditionEditorInner2"},
  2679.                     DIV({class: "conditionEditorInner"},
  2680.                         DIV({class: "conditionCaption"}, $STR("ConditionInput")),
  2681.                         INPUT({class: "conditionInput", type: "text"})
  2682.                     )
  2683.                 )
  2684.             ),
  2685.             DIV({class: "conditionEditorBottom1"},
  2686.                 DIV({class: "conditionEditorBottom2"})
  2687.             )
  2688.         ),
  2689.  
  2690.     show: function(sourceLine, panel, value)
  2691.     {
  2692.         this.target = sourceLine;
  2693.         this.panel = panel;
  2694.  
  2695.         this.getAutoCompleter().reset();
  2696.  
  2697.         hide(this.box, true);
  2698.         panel.selectedSourceBox.appendChild(this.box);
  2699.  
  2700.         this.input.value = value;
  2701.  
  2702.         setTimeout(bindFixed(function()
  2703.         {
  2704.             var offset = getClientOffset(sourceLine);
  2705.  
  2706.             var bottom = offset.y+sourceLine.offsetHeight;
  2707.             var y = bottom - this.box.offsetHeight;
  2708.             if (y < panel.selectedSourceBox.scrollTop)
  2709.             {
  2710.                 y = offset.y;
  2711.                 setClass(this.box, "upsideDown");
  2712.             }
  2713.             else
  2714.                 removeClass(this.box, "upsideDown");
  2715.  
  2716.             this.box.style.top = y + "px";
  2717.             hide(this.box, false);
  2718.  
  2719.             this.input.focus();
  2720.             this.input.select();
  2721.         }, this));
  2722.     },
  2723.  
  2724.     hide: function()
  2725.     {
  2726.         this.box.parentNode.removeChild(this.box);
  2727.  
  2728.         delete this.target;
  2729.         delete this.panel;
  2730.     },
  2731.  
  2732.     layout: function()
  2733.     {
  2734.     },
  2735.  
  2736.     // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  2737.  
  2738.     endEditing: function(target, value, cancel)
  2739.     {
  2740.         if (!cancel)
  2741.         {
  2742.             var sourceFile = this.panel.location;
  2743.             var lineNo = parseInt(this.target.textContent);
  2744.  
  2745.             if (value)
  2746.                 fbs.setBreakpointCondition(sourceFile, lineNo, value, Firebug.Debugger);
  2747.             else
  2748.                 fbs.clearBreakpoint(sourceFile.href, lineNo);
  2749.         }
  2750.     }
  2751. });
  2752.  
  2753. // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  2754.  
  2755. function setLineBreakpoints(sourceFile, sourceBox)
  2756. {
  2757.     fbs.enumerateBreakpoints(sourceFile.href, {call: function(url, line, script, props)
  2758.     {
  2759.         var scriptRow = sourceBox.getLineNode(line);
  2760.         if (scriptRow)
  2761.         {
  2762.             scriptRow.setAttribute("breakpoint", "true");
  2763.             if (props.disabled)
  2764.                 scriptRow.setAttribute("disabledBreakpoint", "true");
  2765.             if (props.condition)
  2766.                 scriptRow.setAttribute("condition", "true");
  2767.         }
  2768.     }});
  2769.  
  2770.  
  2771. }
  2772.  
  2773. function getCallingFrame(frame)
  2774. {
  2775.     try
  2776.     {
  2777.         do
  2778.         {
  2779.             frame = frame.callingFrame;
  2780.             if (!(Firebug.filterSystemURLs && isSystemURL(normalizeURL(frame.script.fileName))))
  2781.                 return frame;
  2782.         }
  2783.         while (frame);
  2784.     }
  2785.     catch (exc)
  2786.     {
  2787.     }
  2788.     return null;
  2789. }
  2790.  
  2791.  
  2792. function getFrameScopeWindowAncestor(frame)  // walk script scope chain to bottom, null unless a Window
  2793. {
  2794.     var scope = frame.scope;
  2795.     if (scope)
  2796.     {
  2797.         while(scope.jsParent)
  2798.             scope = scope.jsParent;
  2799.  
  2800.         if (scope.jsClassName == "Window" || scope.jsClassName == "ChromeWindow")
  2801.             return  scope.getWrappedValue();
  2802.  
  2803.     }
  2804.     else
  2805.         return null;
  2806. }
  2807.  
  2808. function getFrameWindow(frame)
  2809. {
  2810.     var result = {};
  2811.     if (frame.eval("window", "", 1, result))
  2812.     {
  2813.         var win = result.value.getWrappedValue();
  2814.         return getRootWindow(win);
  2815.     }
  2816. }
  2817.  
  2818. function getFrameContext(frame)
  2819. {
  2820.     var win = getFrameScopeWindowAncestor(frame);
  2821.     return win ? TabWatcher.getContextByWindow(win) : null;
  2822. }
  2823.  
  2824. function cacheAllScripts(context)
  2825. {
  2826.     return;
  2827.     // TODO the scripts should all be ready
  2828.     for (var url in context.sourceFileMap)
  2829.         context.sourceFileMap[url].cache(context);
  2830. }
  2831.  
  2832. function countBreakpoints(context)
  2833. {
  2834.     var count = 0;
  2835.     for (var url in context.sourceFileMap)
  2836.     {
  2837.         fbs.enumerateBreakpoints(url, {call: function(url, lineNo)
  2838.         {
  2839.             ++count;
  2840.         }});
  2841.     }
  2842.     return count;
  2843. }
  2844.                                                                                                                        /*@explore*/
  2845.                                                                                                                      /*@explore*/
  2846.  
  2847. // ************************************************************************************************
  2848.  
  2849. Firebug.registerActivableModule(Firebug.Debugger);
  2850. Firebug.registerPanel(BreakpointsPanel);
  2851. Firebug.registerPanel(CallstackPanel);
  2852. Firebug.registerPanel(Firebug.ScriptPanel);
  2853.  
  2854. // ************************************************************************************************
  2855.  
  2856. }});
  2857.